diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 25fa3fb..b1a1554 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] python-version: ["3.10"] steps: - uses: actions/checkout@v2 @@ -19,5 +19,5 @@ jobs: run: | python -m pip install --upgrade pip pip install tox tox-gh-actions - - name: Lint with tox + - name: Test with tox run: tox diff --git a/FOSSDB_web/apps/fossdb/admin.py b/FOSSDB_web/apps/fossdb/admin.py index e69de29..ea74c31 100644 --- a/FOSSDB_web/apps/fossdb/admin.py +++ b/FOSSDB_web/apps/fossdb/admin.py @@ -0,0 +1,35 @@ +from django.contrib import admin + +from .host.models import HostingPlatform, ProjectHostingPlatform +from .language.models import ProgrammingLanguage, ProjectProgrammingLanguage +from .license.models import License +from .models import Project +from .tag.models import Tag + + +class ProjectProgrammingLanguageInline(admin.TabularInline): + model = ProjectProgrammingLanguage + extra = 1 + + +class ProjectHostingPlatformInline(admin.TabularInline): + model = ProjectHostingPlatform + extra = 1 + + +class ProjectAdmin(admin.ModelAdmin): + inlines = [ProjectProgrammingLanguageInline, ProjectHostingPlatformInline] + list_display = ("author", "name", "_languages", "_hosting_platform") + + def _languages(self, object): + return " | ".join([i.language.language for i in object.projectprogramminglanguage_set.all()]) + + def _hosting_platform(self, object): + return " | ".join([i.hosting_platform.hosting_platform for i in object.projecthostingplatform_set.all()]) + + +admin.site.register(HostingPlatform) +admin.site.register(Project, ProjectAdmin) +admin.site.register(License) +admin.site.register(ProgrammingLanguage) +admin.site.register(Tag) diff --git a/FOSSDB_web/apps/fossdb/forms.py b/FOSSDB_web/apps/fossdb/forms.py index 9603689..b1f60e2 100644 --- a/FOSSDB_web/apps/fossdb/forms.py +++ b/FOSSDB_web/apps/fossdb/forms.py @@ -4,6 +4,19 @@ from .models import Project class ProjectForm(forms.ModelForm): + class Meta: model = Project - fields = ["title", "description"] + fields = ["name", "description", "licenses"] + + widgets = { + "name": forms.TextInput(attrs={ + "class": "form-control", + "placeholder": "Project name", + }), + "description": forms.Textarea(attrs={ + "class": "form-control", + "placeholder": "Description", + }), + "licenses": forms.CheckboxSelectMultiple(), + } diff --git a/FOSSDB_web/apps/fossdb/host/forms.py b/FOSSDB_web/apps/fossdb/host/forms.py new file mode 100644 index 0000000..5c04a50 --- /dev/null +++ b/FOSSDB_web/apps/fossdb/host/forms.py @@ -0,0 +1,22 @@ +from django import forms + +from fossdb.models import Project + +from .models import HostingPlatform, ProjectHostingPlatform + + +class HostingPlatformForm(forms.ModelForm): + url = forms.URLField() + + class Meta: + model = HostingPlatform + fields = ["hosting_platform", "url"] + + +ProjectHostingPlatformFormSet = forms.inlineformset_factory( + Project, + ProjectHostingPlatform, + form=HostingPlatformForm, + extra=1, + can_delete=False +) diff --git a/FOSSDB_web/apps/fossdb/host/models.py b/FOSSDB_web/apps/fossdb/host/models.py new file mode 100644 index 0000000..c401aa0 --- /dev/null +++ b/FOSSDB_web/apps/fossdb/host/models.py @@ -0,0 +1,17 @@ +from django.db import models + + +class HostingPlatform(models.Model): + hosting_platform = models.CharField(max_length=100) + + def __str__(self): + return self.hosting_platform + + +class ProjectHostingPlatform(models.Model): + project = models.ForeignKey("Project", on_delete=models.CASCADE) + hosting_platform = models.ForeignKey(HostingPlatform, on_delete=models.CASCADE) + url = models.URLField() + + def __str__(self): + return f"{self.project} | {self.hosting_platform}" diff --git a/FOSSDB_web/apps/fossdb/language/forms.py b/FOSSDB_web/apps/fossdb/language/forms.py new file mode 100644 index 0000000..a7cadf3 --- /dev/null +++ b/FOSSDB_web/apps/fossdb/language/forms.py @@ -0,0 +1,22 @@ +from django import forms + +from fossdb.models import Project + +from .models import ProgrammingLanguage, ProjectProgrammingLanguage + + +class ProgrammingLanguageForm(forms.ModelForm): + percentage = forms.IntegerField(min_value=0, max_value=100) + + class Meta: + model = ProgrammingLanguage + fields = ["language", "percentage"] + + +ProjectProgrammingLanguageFormSet = forms.inlineformset_factory( + Project, + ProjectProgrammingLanguage, + form=ProgrammingLanguageForm, + extra=1, + can_delete=True, +) diff --git a/FOSSDB_web/apps/fossdb/language/models.py b/FOSSDB_web/apps/fossdb/language/models.py new file mode 100644 index 0000000..9a624f1 --- /dev/null +++ b/FOSSDB_web/apps/fossdb/language/models.py @@ -0,0 +1,17 @@ +from django.db import models + + +class ProgrammingLanguage(models.Model): + language = models.CharField(max_length=100) + + def __str__(self): + return self.language + + +class ProjectProgrammingLanguage(models.Model): + project = models.ForeignKey("Project", on_delete=models.CASCADE) + language = models.ForeignKey(ProgrammingLanguage, on_delete=models.CASCADE) + percentage = models.PositiveIntegerField() + + def __str__(self): + return f"{self.project} | {self.language} | {self.percentage}%" diff --git a/FOSSDB_web/apps/fossdb/license/forms.py b/FOSSDB_web/apps/fossdb/license/forms.py new file mode 100644 index 0000000..0c180da --- /dev/null +++ b/FOSSDB_web/apps/fossdb/license/forms.py @@ -0,0 +1,9 @@ +from django import forms + +from .models import License + + +class LicenseForm(forms.ModelForm): + class Meta: + model = License + fields = ["short_name", "full_name", "url", "description"] diff --git a/FOSSDB_web/apps/fossdb/license/models.py b/FOSSDB_web/apps/fossdb/license/models.py new file mode 100644 index 0000000..572a0b4 --- /dev/null +++ b/FOSSDB_web/apps/fossdb/license/models.py @@ -0,0 +1,11 @@ +from django.db import models + + +class License(models.Model): + short_name = models.CharField(max_length=50) + full_name = models.CharField(max_length=100, blank=True, default="") + url = models.URLField(blank=True, default="") + description = models.TextField(blank=True, default="") + + def __str__(self): + return self.short_name diff --git a/FOSSDB_web/apps/fossdb/migrations/0001_initial.py b/FOSSDB_web/apps/fossdb/migrations/0001_initial.py index def7053..a142884 100644 --- a/FOSSDB_web/apps/fossdb/migrations/0001_initial.py +++ b/FOSSDB_web/apps/fossdb/migrations/0001_initial.py @@ -1,8 +1,9 @@ -# Generated by Django 4.1 on 2023-03-27 17:46 +# Generated by Django 4.1.7 on 2023-04-09 10:17 from django.conf import settings from django.db import migrations, models import django.db.models.deletion +import uuid class Migration(migrations.Migration): @@ -15,13 +16,84 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Project', + name='HostingPlatform', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=255)), - ('description', models.TextField()), - ('create_date', models.DateTimeField(auto_now_add=True)), + ('hosting_platform', models.CharField(max_length=100)), + ], + ), + migrations.CreateModel( + name='License', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('short_name', models.CharField(max_length=50)), + ('full_name', models.CharField(blank=True, default='', max_length=100)), + ('url', models.URLField(blank=True, default='')), + ('description', models.TextField(blank=True, default='')), + ], + ), + migrations.CreateModel( + name='ProgrammingLanguage', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('language', models.CharField(max_length=100)), + ], + ), + migrations.CreateModel( + name='Project', + fields=[ + ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=255)), + ('description', models.TextField(blank=True, default='')), + ('date_created', models.DateTimeField(auto_now_add=True)), ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], ), + migrations.CreateModel( + name='Tag', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('description', models.TextField(blank=True, default='')), + ('icon', models.ImageField(blank=True, null=True, upload_to='types/icons/')), + ], + ), + migrations.CreateModel( + name='ProjectProgrammingLanguage', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('percentage', models.PositiveIntegerField()), + ('language', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fossdb.programminglanguage')), + ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fossdb.project')), + ], + ), + migrations.CreateModel( + name='ProjectHostingPlatform', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('url', models.URLField()), + ('hosting_platform', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fossdb.hostingplatform')), + ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fossdb.project')), + ], + ), + migrations.AddField( + model_name='project', + name='hosting_platform', + field=models.ManyToManyField(related_name='projects', through='fossdb.ProjectHostingPlatform', to='fossdb.hostingplatform'), + ), + migrations.AddField( + model_name='project', + name='licenses', + field=models.ManyToManyField(to='fossdb.license'), + ), + migrations.AddField( + model_name='project', + name='programming_languages', + field=models.ManyToManyField(related_name='projects', through='fossdb.ProjectProgrammingLanguage', to='fossdb.programminglanguage'), + ), + migrations.AddField( + model_name='project', + name='tag', + field=models.ManyToManyField(to='fossdb.tag'), + ), ] diff --git a/FOSSDB_web/apps/fossdb/migrations/0002_operatingsystem_project_os.py b/FOSSDB_web/apps/fossdb/migrations/0002_operatingsystem_project_os.py new file mode 100644 index 0000000..9741c32 --- /dev/null +++ b/FOSSDB_web/apps/fossdb/migrations/0002_operatingsystem_project_os.py @@ -0,0 +1,27 @@ +# Generated by Django 4.1.7 on 2023-04-09 10:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('fossdb', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='OperatingSystem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=100)), + ('description', models.TextField(blank=True, default='')), + ('version', models.CharField(blank=True, max_length=50)), + ], + ), + migrations.AddField( + model_name='project', + name='os', + field=models.ManyToManyField(to='fossdb.operatingsystem'), + ), + ] diff --git a/FOSSDB_web/apps/fossdb/migrations/0003_star_project_star.py b/FOSSDB_web/apps/fossdb/migrations/0003_star_project_star.py new file mode 100644 index 0000000..2baf97f --- /dev/null +++ b/FOSSDB_web/apps/fossdb/migrations/0003_star_project_star.py @@ -0,0 +1,29 @@ +# Generated by Django 4.1.7 on 2023-04-09 10:51 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('fossdb', '0002_operatingsystem_project_os'), + ] + + operations = [ + migrations.CreateModel( + name='Star', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='stars', to='fossdb.project')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + migrations.AddField( + model_name='project', + name='star', + field=models.ManyToManyField(related_name='projects_star', to='fossdb.star'), + ), + ] diff --git a/FOSSDB_web/apps/fossdb/models.py b/FOSSDB_web/apps/fossdb/models.py index e61e920..53d1bd0 100644 --- a/FOSSDB_web/apps/fossdb/models.py +++ b/FOSSDB_web/apps/fossdb/models.py @@ -1,15 +1,42 @@ -from django.contrib.auth.models import User +import uuid + +from django.conf import settings from django.db import models +from .host.models import HostingPlatform +from .language.models import ProgrammingLanguage +from .license.models import License +from .operating_system.models import OperatingSystem +from .star.models import Star +from .tag.models import Tag + +User = settings.AUTH_USER_MODEL + class Project(models.Model): - author = models.ForeignKey(User, on_delete=models.CASCADE) - title = models.CharField(max_length=255) - description = models.TextField() - create_date = models.DateTimeField(auto_now_add=True) + uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + author = models.ForeignKey(User, on_delete=models.CASCADE, db_index=True) + name = models.CharField(max_length=255) + description = models.TextField(blank=True, default="") + licenses = models.ManyToManyField(License) + programming_languages = models.ManyToManyField(ProgrammingLanguage, through="ProjectProgrammingLanguage", related_name="projects") + hosting_platform = models.ManyToManyField(HostingPlatform, through="ProjectHostingPlatform", related_name="projects") + tag = models.ManyToManyField(Tag) + os = models.ManyToManyField(OperatingSystem) + star = models.ManyToManyField(Star, related_name="projects_star") + date_created = models.DateTimeField(auto_now_add=True) - def __str__(self): - return self.repo_name + @property + def star_amount(self): + return self.star.count() def get_absolute_url(self): - return f"/projects/{self.id}" + return f"/{self.author.name}/{self.name}" + + def __str__(self): + return f"{self.author} | {self.name}" + + def save(self, *args, **kwargs): + if not self.uuid: + self.uuid = uuid.uuid3(uuid.uuid4(), f"{self.author.username}-{self.name}") + super().save(*args, **kwargs) diff --git a/FOSSDB_web/apps/fossdb/operating_system/models.py b/FOSSDB_web/apps/fossdb/operating_system/models.py new file mode 100644 index 0000000..d638ec8 --- /dev/null +++ b/FOSSDB_web/apps/fossdb/operating_system/models.py @@ -0,0 +1,7 @@ +from django.db import models + + +class OperatingSystem(models.Model): + name = models.CharField(max_length=100) + description = models.TextField(blank=True, default="") + version = models.CharField(max_length=50, blank=True) diff --git a/FOSSDB_web/apps/fossdb/star/models.py b/FOSSDB_web/apps/fossdb/star/models.py new file mode 100644 index 0000000..74f0452 --- /dev/null +++ b/FOSSDB_web/apps/fossdb/star/models.py @@ -0,0 +1,9 @@ +from django.conf import settings +from django.db import models + +User = settings.AUTH_USER_MODEL + + +class Star(models.Model): + user = models.ForeignKey(User, on_delete=models.CASCADE) + project = models.ForeignKey("Project", on_delete=models.CASCADE, related_name="stars") diff --git a/FOSSDB_web/apps/fossdb/tag/models.py b/FOSSDB_web/apps/fossdb/tag/models.py new file mode 100644 index 0000000..10bb71e --- /dev/null +++ b/FOSSDB_web/apps/fossdb/tag/models.py @@ -0,0 +1,11 @@ + +from django.db import models + + +class Tag(models.Model): + name = models.CharField(max_length=100) + description = models.TextField(blank=True, default="") + icon = models.ImageField(upload_to="types/icons/", null=True, blank=True) + + def __str__(self): + return self.name diff --git a/FOSSDB_web/apps/fossdb/views.py b/FOSSDB_web/apps/fossdb/views.py index 3b3a3a8..4d76938 100644 --- a/FOSSDB_web/apps/fossdb/views.py +++ b/FOSSDB_web/apps/fossdb/views.py @@ -2,24 +2,49 @@ from django.contrib.auth.decorators import login_required, permission_required from django.shortcuts import redirect, render from .forms import ProjectForm +from .host.forms import ProjectHostingPlatformFormSet +from .language.forms import ProjectProgrammingLanguageFormSet from .models import Project def index(request): - projects = Project.objects.all() - return render(request, "fossdb/index.html", {"title": "FOSSDB", "projects": projects}) + context = { + "title": "FOSSDB", + "projects": Project.objects.all(), + } + return render(request, "fossdb/index.html", context) @login_required(login_url="login/") -@permission_required("fossdb.add_post)", login_url="login/", raise_exception=True) +@permission_required("fossdb.add_project", login_url="login/", raise_exception=True) def add_project(request): if request.method == "POST": - form = ProjectForm(request.POST) - if form.is_valid(): - post = form.save(commit=False) - post.author = request.user - post.save() + project_form = ProjectForm(request.POST) + language_formset = ProjectProgrammingLanguageFormSet(request.POST, instance=Project()) + host_formset = ProjectHostingPlatformFormSet(request.POST, instance=Project()) + + if project_form.is_valid() and language_formset.is_valid() and host_formset.is_valid(): + project = project_form.save(commit=False) + project.author = request.user + project.save() + + language_formset.instance = project + language_formset.save() + + host_formset.instance = project + host_formset.save() + + project_form.save_m2m() return redirect("/") else: - form = ProjectForm() - return render(request, "fossdb/add_project.html", {"title": "Add project", "form": form}) + project_form = ProjectForm() + language_formset = ProjectProgrammingLanguageFormSet() + host_formset = ProjectHostingPlatformFormSet() + + context = { + "title": "Add project", + "project_form": project_form, + "language_formset": language_formset, + "host_formset": host_formset, + } + return render(request, "fossdb/add_project.html", context) diff --git a/FOSSDB_web/settings.py b/FOSSDB_web/settings.py index e4d3655..0c10737 100644 --- a/FOSSDB_web/settings.py +++ b/FOSSDB_web/settings.py @@ -36,8 +36,8 @@ ALLOWED_HOSTS = config["ALLOWED_HOSTS"] # Application definition INSTALLED_APPS = [ - "fossdb", "account", + "fossdb", "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", @@ -82,7 +82,7 @@ WSGI_APPLICATION = "FOSSDB_web.wsgi.application" DATABASES = { "default": { - "ENGINE": "django.db.backends.mysql", + "ENGINE": f"django.db.backends.{config['DATABASE']['ENGINE']}", "NAME": config["DATABASE"]["NAME"], "USER": config["DATABASE"]["USER"], "PASSWORD": config["DATABASE"]["PASSWORD"], @@ -128,6 +128,7 @@ USE_TZ = True STATIC_URL = "/static/" STATIC_ROOT = BASE_PATH.joinpath("static") +MEDIA_URL = "/media/" MEDIA_ROOT = BASE_PATH.joinpath("media") # Default primary key field type @@ -136,3 +137,14 @@ MEDIA_ROOT = BASE_PATH.joinpath("media") DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" LOGIN_REDIRECT_URL = "/" LOGOUT_REDIRECT_URL = "/" + +# HTTPS settings +# SESSION_COOKIE_SECURE = True +# CSRF_COOKIE_SECURE = True +# SECURE_SSL_REDIRECT = True + +# HSTS settings +# SECURE_HSTS_SECONDS = 31536000 # 1 year +# SECURE_HSTS_SECONDS = 1 # 1 year +# SECURE_HSTS_PRELOAD = True +# SECURE_HSTS_INCLUDE_SUBDOMAINS = True diff --git a/config.json b/config.json index dcaea64..2871971 100644 --- a/config.json +++ b/config.json @@ -1,10 +1,8 @@ { "SECRET_KEY": "", - "ALLOWED_HOSTS": [ - "", - "localhost" - ], + "ALLOWED_HOSTS": [""], "DATABASE": { + "ENGINE": "", "NAME": "", "USER": "", "PASSWORD": "", diff --git a/requirements.txt b/requirements.txt index 4e4add1..c689f93 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ Django==4.1.7 +Pillow==9.4.0 fontawesomefree==6.2.1 mysqlclient==2.1.1 diff --git a/setup.cfg b/setup.cfg index 069d76d..3ef6ae0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,6 +12,7 @@ classifiers = packages = FOSSDB_web install_requires = Django>=4.1 + Pillow>=9.4.0 fontawesomefree>=6.2.1 mysqlclient>=2.1.1 diff --git a/templates/fossdb/add_project.html b/templates/fossdb/add_project.html index 52b018a..d414e16 100644 --- a/templates/fossdb/add_project.html +++ b/templates/fossdb/add_project.html @@ -4,7 +4,19 @@ {% block meta %}{% endblock %} {% block content %}
{% endblock %} diff --git a/templates/fossdb/index.html b/templates/fossdb/index.html index c7843d8..3b062bb 100644 --- a/templates/fossdb/index.html +++ b/templates/fossdb/index.html @@ -3,10 +3,35 @@ {% block title %}{{ title }}{% endblock %} {% block meta %}{% endblock %} {% block content %} + + +{{ project.description }}
+No license
+ {% endfor %} +No language
+ {% endfor %} +{{ project.date_created }}
{% empty %}No projects yet (
{% endfor %}