feat: create initial Django project for submissions

Initialized a new Django project named "dbjsubmissions" with essential
configuration and initial app setup. Added .gitignore to ignore common
unwanted files.

Implemented models, views, forms, templates, and urls for managing
manuscript submissions. Configured settings for basic functionality,
including required third-party packages in requirements.txt.

Supports user registration, login, and manuscript submission workflows.
This commit is contained in:
Kumi 2024-09-03 20:57:52 +02:00
commit d6f549d5be
Signed by: kumi
GPG key ID: ECBCC9082395383F
23 changed files with 515 additions and 0 deletions

5
.gitignore vendored Normal file
View file

@ -0,0 +1,5 @@
config.ini
venv/
*.pyc
__pycache__/
db.sqlite3

View file

16
dbjsubmissions/asgi.py Normal file
View file

@ -0,0 +1,16 @@
"""
ASGI config for dbjsubmissions project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dbjsubmissions.settings')
application = get_asgi_application()

110
dbjsubmissions/settings.py Normal file
View file

@ -0,0 +1,110 @@
from pathlib import Path
from autosecretkey import AutoSecretKey
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent
ASK = AutoSecretKey('config.ini')
SECRET_KEY = ASK.secret_key
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ["*"]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'dbjsubmissions.submissions',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'dbjsubmissions.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'dbjsubmissions.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

View file

View file

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class SubmissionsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'dbjsubmissions.submissions'

View file

@ -0,0 +1,14 @@
from django import forms
from .models import Submission
class SubmissionForm(forms.ModelForm):
class Meta:
model = Submission
fields = [
"title",
"author_full_name",
"institution_affiliation",
"corresponding_author_contact",
"pdf_file",
]

View file

@ -0,0 +1,29 @@
import uuid
from django.db import models
from django.contrib.auth.models import User
class Submission(models.Model):
STATUS_CHOICES = [
('submitted', 'Submitted'),
('under_review', 'Under Review'),
('accepted', 'Accepted'),
('rejected', 'Rejected'),
]
user = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
author_full_name = models.CharField(max_length=255)
institution_affiliation = models.TextField()
corresponding_author_contact = models.TextField()
pdf_file = models.FileField(upload_to='submissions/')
submission_date = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='submitted')
unique_code = models.CharField(max_length=27, unique=True, editable=False)
def save(self, *args, **kwargs):
if not self.unique_code:
self.unique_code = uuid.uuid4().hex[:27].upper()
super().save(*args, ** kwargs)
def __str__(self):
return self.title

View file

@ -0,0 +1,106 @@
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}Duck Behavior Journal{% endblock %}</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #e0e0e0;
color: #333;
margin: 0;
padding: 0;
}
.container {
width: 80%;
margin: 0 auto;
background-color: #fff;
padding: 20px;
border: 1px solid #ccc;
box-shadow: 2px 2px 12px rgba(0, 0, 0, 0.1);
}
header {
background-color: #004d80;
color: #fff;
padding: 10px;
text-align: center;
}
nav {
background-color: #003366;
overflow: hidden;
}
nav a {
float: left;
display: block;
color: white;
text-align: center;
padding: 14px 16px;
text-decoration: none;
}
nav a:hover {
background-color: #002244;
}
footer {
background-color: #004d80;
color: #fff;
padding: 10px;
text-align: center;
position: fixed;
bottom: 0;
width: 100%;
}
table.form-table {
width: 100%;
margin: 20px 0;
border-collapse: collapse;
}
table.form-table td {
padding: 10px;
border: 1px solid #ccc;
}
table.form-table td label {
display: block;
margin-bottom: 5px;
}
table.form-table td input[type="text"],
table.form-table td input[type="file"],
table.form-table td select {
width: 100%;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
background-color: #004d80;
color: #fff;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #003366;
}
</style>
</head>
<body>
<header>
<h1>Duck Behavior Journal</h1>
</header>
<nav>
<a href="/">Home</a>
<a href="/submit/">Submit Manuscript</a>
{% if user.is_authenticated %}
<a href="{% url 'logout' %}">Logout</a>
{% else %}
<a href="{% url 'login' %}">Login</a>
{% endif %}
</nav>
<div class="container">
{% block content %}
{% endblock %}
</div>
<footer>
<p>&copy; 2023 Duck Behavior Journal</p>
</footer>
</body>
</html>

View file

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block title %}Login{% endblock %}
{% block content %}
<h2>Login</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Login</button>
</form>
<p>Don't have an account? <a href="{% url 'register' %}">Register here</a>.</p>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block title %}Register{% endblock %}
{% block content %}
<h2>Register</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Register</button>
</form>
<p>Already have an account? <a href="{% url 'login' %}">Login here</a>.</p>
{% endblock %}

View file

@ -0,0 +1,31 @@
{% extends "base.html" %}
{% block title %}My Submissions{% endblock %}
{% block content %}
<h2>My Submissions</h2>
<table class="form-table">
<thead>
<tr>
<th>Title</th>
<th>Submission Date</th>
<th>Status</th>
<th>Unique Code</th>
</tr>
</thead>
<tbody>
{% for submission in submissions %}
<tr>
<td>{{ submission.title }}</td>
<td>{{ submission.submission_date }}</td>
<td>{{ submission.get_status_display }}</td>
<td>{{ submission.unique_code }}</td>
</tr>
{% empty %}
<tr>
<td colspan="4">No submissions found.</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block title %}Submission Success{% endblock %}
{% block content %}
<h2>Submission Successful!</h2>
<p>Your manuscript has been successfully submitted.</p>
{% endblock %}

View file

@ -0,0 +1,47 @@
{% extends "base.html" %}
{% block title %}Submit Manuscript{% endblock %}
{% block content %}
<h2>Submit Your Manuscript</h2>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<table class="form-table">
<tr>
<td><label for="id_title">Title</label></td>
<td>{{ form.title }}</td>
</tr>
<tr>
<td><label for="id_author_full_name">Author Full Name</label></td>
<td>{{ form.author_full_name }}</td>
</tr>
<tr>
<td><label for="id_institution_affiliation">Institution Affiliation</label></td>
<td>{{ form.institution_affiliation }}</td>
</tr>
<tr>
<td><label for="id_corresponding_author_contact">Corresponding Author Contact</label></td>
<td>{{ form.corresponding_author_contact }}</td>
</tr>
<tr>
<td><label for="id_pdf_file">PDF File</label></td>
<td>{{ form.pdf_file }}</td>
</tr>
<tr>
<td><label for="id_mother_maiden_name">Mother's Maiden Name</label></td>
<td>{{ form.mother_maiden_name }}</td>
</tr>
<tr>
<td><label for="id_favorite_duck_species">Favorite Species of Duck</label></td>
<td>{{ form.favorite_duck_species }}</td>
</tr>
<tr>
<td><label for="id_quack_count">Number of Times "Quack" Appears</label></td>
<td>{{ form.quack_count }}</td>
</tr>
</table>
<div style="text-align: center;">
<button type="submit">Submit</button>
</div>
</form>
{% endblock %}

View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View file

@ -0,0 +1,11 @@
from django.contrib import admin
from django.urls import path, include
from dbjsubmissions.submissions.views import IndexView, SubmissionCreateView, SubmissionSuccessView, RegisterView
urlpatterns = [
path('admin/', admin.site.urls),
path('', IndexView.as_view(), name='index'),
path('submit/', SubmissionCreateView.as_view(), name='submit_manuscript'),
path('submission_success/', SubmissionSuccessView.as_view(), name='submission_success'),
path('accounts/register/', RegisterView.as_view(), name='register'),
]

View file

@ -0,0 +1,51 @@
from django.urls import reverse_lazy
from django.views.generic import ListView, CreateView, TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login
from django.shortcuts import redirect
from .models import Submission
from .forms import SubmissionForm
class IndexView(LoginRequiredMixin, ListView):
model = Submission
template_name = 'submissions/index.html'
context_object_name = 'submissions'
def get_queryset(self):
return Submission.objects.filter(user=self.request.user)
class SubmissionCreateView(LoginRequiredMixin, CreateView):
model = Submission
form_class = SubmissionForm
template_name = 'submissions/submit_manuscript.html'
success_url = reverse_lazy('submission_success')
def form_valid(self, form):
form.instance.user = self.request.user
response = super().form_valid(form)
self.request.session['unique_code'] = form.instance.unique_code
return response
class SubmissionSuccessView(LoginRequiredMixin, TemplateView):
template_name = 'submissions/submission_success.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(** kwargs)
context['unique_code'] = self.request.session.get('unique_code', '')
return context
class RegisterView(CreateView):
model = User
form_class = UserCreationForm
template_name = 'registration/register.html'
success_url = reverse_lazy('login')
def form_valid(self, form):
valid = super(RegisterView, self).form_valid(form)
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password1')
user = authenticate(username=username, password=password)
login(self.request, user)
return valid

8
dbjsubmissions/urls.py Normal file
View file

@ -0,0 +1,8 @@
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('accounts/', include('django.contrib.auth.urls')),
path('', include('dbjsubmissions.submissions.urls')),
]

16
dbjsubmissions/wsgi.py Normal file
View file

@ -0,0 +1,16 @@
"""
WSGI config for dbjsubmissions project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dbjsubmissions.settings')
application = get_wsgi_application()

22
manage.py Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'dbjsubmissions.settings')
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

3
requirements.txt Normal file
View file

@ -0,0 +1,3 @@
Django
psycopg2-binary
django-autosecretkey