Skip to content
Snippets Groups Projects
Commit 652ed258 authored by Janis Daniel Dähne's avatar Janis Daniel Dähne
Browse files

- started upload files

- the basic structure is ensured
  - for new and new external (ostepu)  upload dirs are created
parent 2a002472
No related branches found
No related tags found
No related merge requests found
......@@ -8,3 +8,4 @@ src/ClientServer/project.lock.json
src/ClientServer/wwwroot/*
!src/ClientServer/wwwroot/.gitkeep
src/ClientServer/output
src/ClientServer/wwwUploaded_Files/*
......@@ -13,19 +13,18 @@ namespace ClientServer.Helpers
/// </summary>
public static class Constants
{
public static string VersionString = "2.2.13";
public static string VersionString = "2.3.0";
/// <summary>
/// this is only set once at program.cs!!
/// </summary>
public static string rootDir = "";
/// <summary>
/// the port to use
/// </summary>
public static int Port = 5000;
/// <summary>
/// a default secret for the jwt tokens (can be changed through the config
/// </summary>
[Obsolete("currently not used, the jwt tokens are not encrypted")]
public static string AuthTokenSecret = "mysupersecret_secretkey!123";
/// <summary>
/// break the automatic assessment loop after an assessment threw an error (e.g. test server not reachable...)
///
......@@ -33,6 +32,32 @@ namespace ClientServer.Helpers
/// </summary>
public static bool BreakAutoAssessmentAfterFirstError = false;
/// <summary>
/// the relative path to the files dir (relative to the root dir)
/// there are all files stored that are uploaded
/// </summary>
public static string FileUploadDirRelativePath = "wwwUploaded_Files";
/// <summary>
/// here are all files stored for the exercise description (usable in markdown) (not custom projects)
/// </summary>
public static string MarkdownFilesUploadDirName = "markdown";
/// <summary>
/// here are all files stored for tests from exercises (not custom projects)
/// </summary>
public static string TestsFilesUploadDirName = "tests";
/// <summary>
/// here are all files stored for all user related files
/// we have a dir for every user
/// so here are all files for customProject and customTests
/// </summary>
public static string UserFilesUploadDirName = "users";
/// <summary>
/// the request timeout to ask ostepu for data
/// </summary>
public static int OstepuRequestTimeoutInMs = 5000;
//the internal names MUST exists in the db
/// <summary>
......
This diff is collapsed.
......@@ -5,6 +5,8 @@ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using ClientServer.Db;
using Microsoft.EntityFrameworkCore;
namespace ClientServer.Helpers
{
......@@ -49,5 +51,172 @@ namespace ClientServer.Helpers
// ASCII encoding replaces non-ascii with question marks, so we use UTF8 to see if multi-byte sequences are there
return Encoding.UTF8.GetByteCount(value) == value.Length;
}
/// <summary>
/// ensures that the upload files structure is present
/// </summary>
public static void CreateUploadFilesStructureIfNotExists()
{
var baseUploadFilesPath = Path.Combine(Constants.rootDir, Constants.FileUploadDirRelativePath);
var markdownFilesPath = Path.Combine(Constants.rootDir, Constants.FileUploadDirRelativePath, Constants.MarkdownFilesUploadDirName);
var testFilesPath = Path.Combine(Constants.rootDir, Constants.FileUploadDirRelativePath, Constants.TestsFilesUploadDirName);
var userFilesPath = Path.Combine(Constants.rootDir, Constants.FileUploadDirRelativePath, Constants.UserFilesUploadDirName);
DirectoryInfo userFilesInfo = null;
Console.WriteLine("[INFO] Ensuring upload files structure...");
try
{
var baseUploadFilesInfo = new DirectoryInfo(baseUploadFilesPath);
if (!baseUploadFilesInfo.Exists)
{
baseUploadFilesInfo.Create();
Console.WriteLine("[INFO] created base upload dir");
}
var markdownFilesInfo = new DirectoryInfo(markdownFilesPath);
var testFilesInfo = new DirectoryInfo(testFilesPath);
userFilesInfo = new DirectoryInfo(userFilesPath);
if (!markdownFilesInfo.Exists)
{
markdownFilesInfo.Create();
Console.WriteLine("[INFO] created markdown (exercise description) files upload dir");
}
if (!testFilesInfo.Exists)
{
testFilesInfo.Create();
Console.WriteLine("[INFO] created test files upload dir");
}
if (!userFilesInfo.Exists)
{
userFilesInfo.Create();
Console.WriteLine("[INFO] created user files upload dir");
userFilesInfo.Refresh();
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
//then we need to ensure every user has a dir...
var optionsBuilder = new DbContextOptionsBuilder<SyndromDbContext>();
optionsBuilder.UseNpgsql(AppConfiguration.DbConnectionString);
int currentUserId = -1;
try
{
List<int> allUserIds;
using (var _context = new SyndromDbContext(optionsBuilder.Options))
{
allUserIds = _context.Users
.Select(p => p.Id)
.OrderBy(p => p)
.ToList()
;
}
for (int i = 0; i < allUserIds.Count; i++)
{
var userId = currentUserId = allUserIds[i];
var userFilesDirPath = Path.Combine(userFilesInfo.FullName, userId.ToString());
var userFilesDirInfo = new DirectoryInfo(userFilesDirPath);
if (!userFilesDirInfo.Exists)
{
userFilesDirInfo.Create();
Console.WriteLine($"[INFO] created user upload files dir for user id {userId}");
}
}
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] cannot creating user (id: {currentUserId}) upload file dir: " + ex);
throw ex;
}
Console.WriteLine("[INFO] ... Finished ensuring upload files structure");
}
/// <summary>
/// creates the user upload file dir (currently only one dir) for a single user
/// THIS REQUIRES that the base upload file structure already exists (else an error is thrown)
/// </summary>
/// <param name="rootDir"></param>
/// <param name="relativeDirToRoot"></param>
/// <param name="userId"></param>
public static void CreateUploadFileStructureForSingleUser(int userId)
{
var baseUploadFilesPath = Path.Combine(Constants.rootDir, Constants.FileUploadDirRelativePath);
var userFilesPath = Path.Combine(Constants.rootDir, Constants.FileUploadDirRelativePath, Constants.UserFilesUploadDirName);
DirectoryInfo userFilesInfo = null;
Console.WriteLine($"[INFO] creating user files upload dir for user id: {userId} ...");
try
{
var baseUploadFilesInfo = new DirectoryInfo(baseUploadFilesPath);
if (!baseUploadFilesInfo.Exists)
{
throw new Exception("base upload file directory not exists");
}
userFilesInfo = new DirectoryInfo(userFilesPath);
if (!userFilesInfo.Exists)
{
throw new Exception("base users upload file directory not exists");
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
try
{
var userFilesDirPath = Path.Combine(userFilesInfo.FullName, userId.ToString());
var userFilesDirInfo = new DirectoryInfo(userFilesDirPath);
if (!userFilesDirInfo.Exists)
{
userFilesDirInfo.Create();
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
Console.WriteLine($"[INFO] ... Finished creating user files upload dir for user id: {userId}");
}
}
}
......@@ -14,9 +14,8 @@ namespace ClientServer
public static void Main(string[] args)
{
// var test = ReleaseHelper.GetNewReleaseCode();
var root = Directory.GetCurrentDirectory();
Constants.rootDir = root;
var builder = new WebHostBuilder()
.UseContentRoot(root)
......@@ -33,6 +32,20 @@ namespace ClientServer
//http://localhost:5000 //see https://github.com/aspnet/KestrelHttpServer/issues/639
.Build();
//we need this for migration (old users that have no upload dir)...
//if we have manyyyyyy users this slowdowns startup hard!!
try
{
Files.CreateUploadFilesStructureIfNotExists();
}
catch (Exception e)
{
Console.WriteLine("[ERROR] Cannot create upload file structure");
Console.WriteLine(e);
throw;
}
if (AppConfiguration.IsSlave == false)
{
ReleaseScheduler.NextRun(); //this is sync (because fast)
......
# Readme
Install instructions can be found at https://gitlab.informatik.uni-halle.de/Syndrom/Docs/blob/master/how_to_install.md
---
## Hints
......@@ -26,68 +32,3 @@ if you want to return `Solution.SolutionFiles` make sure to always replace the r
to do so use the helper `UserSolutionHelper`
# exercise code template handling...
Der Plan ist etwas von beiden Methoden zu nutzen...
- Schreibgeschützte Dateien werden wie alle anderen Dateien auch in der Solution angelegt (kopiert)
- Schreibgeschützte Dateien können immer vom Tutor geändert werden können und das Backend ersetzt den Inhalt schreibgeschützter Dateien von Solutions immer beim Senden (in der Response) mit der Vorgabe (CodeTemplate)
## Szenarien 1
Es muss unterschieden werden, ob eine initiale Solution angelegt wird (1) oder bereits eine Solution existiert (2)...
`reset code` entspricht dem initialen Anlegen einer Solution
### Tutor erstellt Datei
1. Neue Datei wird mit der Solution aus dem Template erstellt
2. Nichts passiert (da z.B. eine Datei mit dem Namen bei Solutions existieren könnte)
(es kann zu Namenskonfliken mit selbst erstellten Dateien vom Benutzer kommen)
### Tutor erstellt schreibgeschützte Datei
1. Neue schreibgeschützte Datei wird mit der Solution aus dem Template erstellt
2. Nichts passiert (da z.B. eine Datei mit dem Namen bei Solutions existieren könnte)
(es kann zu Namenskonfliken mit selbst erstellten Dateien vom Benutzer kommen)
## Szenarien 2
### Tutor ändert Datei nachträglich (nachdem Solutions mit den alten Dateien existieren)
Nichts passiert (da die Datei sehr wahrscheinlich vom Benutzer angepasst wurde)
### Tutor ändert schreibgeschützte Datei nachträglich
Nichts passiert (da das Backend schreibgeschützte Dateien beim Senden ersetzt)
### Tutor löscht Datei
Nichts passiert (da dann Code vom Benutzer gelöscht wird)
### Tutor löscht schreibgeschützte Datei
Nichts passiert (die Datei existiert weiter in der Solution des Benutzers) (TODO ist das gut?)
### Tutor ändert Datei zu schreibgeschützt nachträglich
Nichts passiert (Datei existiert schon, Inhalt wird beim Senden überschrieben
Code des Benutzer ist nicht weg, kann aber durch die aktuelle API nicht abgefragt werden)
*Datei muss nicht existieren, wenn diese nachträglich angelegt wurde (siehe Szenario 2)*
### Tutor ändert schreibgeschützt Datei zu *normaler* Datei nachträglich
Nichts passiert (Datei existiert schon, der Inhalt entspricht allerdings dem Inhalt der Datei, wenn sie zum letzten mal vom Benutzer bearbeitet wurde oder vom initialen Anlegen)
*Datei muss nicht existieren, wenn diese nachträglich angelegt wurde (siehe Szenario 2)*
## Weiteres
Da beim Erstellen und Löschen von Dateien nichts passiert und neue Solutions dann mehr/weniger Dateien haben als ältere Solutions, sollte man das Erstellen und Löschen von Dateien wohl verbieten, wenn es mindestens 1 Freigabe für die Aufgabe gibt.
Das ist auch wichtig, da man sonst nicht mehr unterscheiden kann, ob eine Datei hinzugefügt oder gelöscht werden soll (*foreach old file check if frontend version exists (and reverse)... if not? deleted in frontend or added* irgendwie sowas)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment