Compare commits

..

No commits in common. "c19f4a815984ebf94304c32c29a5838af0dd8a28" and "39e40d62f4074d6dd1978e95619191351e78ddb0" have entirely different histories.

17 changed files with 12 additions and 199 deletions

View File

@ -1,2 +1 @@
start_server.sh start_server.sh
run/*

View File

@ -2,12 +2,6 @@
# Example configuration. Must be edited and copied to ".env" next to the compose.yaml # Example configuration. Must be edited and copied to ".env" next to the compose.yaml
#################################################################################################### ####################################################################################################
# User id to use for the web application. This determines the user id, the media and static files are written to the volumes.
# Make sure the user has rw access to these directories.
DJANGO_USER_ID=1000
# Group id to use for the web application
DJANGO_USER_GID=1000
# Path to to mount as the directory for static data. Must be served by a webserver on the /static path # Path to to mount as the directory for static data. Must be served by a webserver on the /static path
DJANGO_STATIC_VOL=/path/to/static/root DJANGO_STATIC_VOL=/path/to/static/root

View File

@ -1,11 +1,6 @@
x-op-restart-policy: &restart_policy
restart: unless-stopped
services: services:
shimatta-kenkyusho-web: shimatta-kenkyusho-web:
<<: *restart_policy
build: . build: .
user: "${DJANGO_USER_ID}:${DJANGO_USER_GID}"
volumes: volumes:
- "${DJANGO_STATIC_VOL:-./run/static}:/var/static" - "${DJANGO_STATIC_VOL:-./run/static}:/var/static"
- "${DJANGO_MEDIA_VOL:-./run/media}:/var/media" - "${DJANGO_MEDIA_VOL:-./run/media}:/var/media"
@ -35,7 +30,6 @@ services:
start_period: 30s start_period: 30s
shimatta-kenkyusho-db: shimatta-kenkyusho-db:
<<: *restart_policy
image: postgres:16.5-alpine image: postgres:16.5-alpine
environment: environment:
POSTGRES_PASSWORD: "${DJANGO_POSTGRESQL_PW:-p4ssw0rd}" POSTGRES_PASSWORD: "${DJANGO_POSTGRESQL_PW:-p4ssw0rd}"

View File

@ -3,6 +3,4 @@ source /home/shimatta/kenkyusho/.venv/bin/activate
cd /home/shimatta/kenkyusho/shimatta_kenkyusho cd /home/shimatta/kenkyusho/shimatta_kenkyusho
python manage.py migrate --settings shimatta_kenkyusho.settings_production python manage.py migrate --settings shimatta_kenkyusho.settings_production
python manage.py collectstatic --settings shimatta_kenkyusho.settings_production --noinput python manage.py collectstatic --settings shimatta_kenkyusho.settings_production --noinput
python manage.py create_kenkyusho_admin_user --settings shimatta_kenkyusho.settings_production
gunicorn -w 4 --bind 0.0.0.0:8000 shimatta_kenkyusho.wsgi:application gunicorn -w 4 --bind 0.0.0.0:8000 shimatta_kenkyusho.wsgi:application

View File

@ -2,6 +2,5 @@
source /home/shimatta/kenkyusho/.venv/bin/activate source /home/shimatta/kenkyusho/.venv/bin/activate
cd /home/shimatta/kenkyusho/shimatta_kenkyusho cd /home/shimatta/kenkyusho/shimatta_kenkyusho
python manage.py migrate --settings shimatta_kenkyusho.settings_production python manage.py migrate --settings shimatta_kenkyusho.settings_production
python manage.py create_kenkyusho_admin_user --settings shimatta_kenkyusho.settings_production
python manage.py runserver 0.0.0.0:8000 --settings shimatta_kenkyusho.settings_production python manage.py runserver 0.0.0.0:8000 --settings shimatta_kenkyusho.settings_production

View File

@ -31,6 +31,5 @@ setuptools==75.3.0
sqlparse==0.4.1 sqlparse==0.4.1
toml==0.10.2 toml==0.10.2
typing_extensions==4.12.2 typing_extensions==4.12.2
tzdata==2024.2
urllib3==2.2.3 urllib3==2.2.3
wrapt==1.12.1 wrapt==1.12.1

View File

@ -252,19 +252,15 @@ class DistributorNumberDeleteForm(forms.Form):
class ComponentParameterDeleteForm(forms.Form): class ComponentParameterDeleteForm(forms.Form):
param_num = forms.UUIDField(required=True) param_num = forms.UUIDField(required=True)
model = parts_models.ComponentParameter
def clean_param_num(self): def clean_param_num(self):
my_uuid = self.cleaned_data['param_num'] my_uuid = self.cleaned_data['param_num']
try: try:
param = self.model.objects.get(id=my_uuid) param = parts_models.ComponentParameter.objects.get(id=my_uuid)
except: except:
raise ValidationError('Parameter Number Invalid') raise ValidationError('Parameter Number Invalid')
return param return param
class PackageParameterDeleteForm(ComponentParameterDeleteForm):
model = parts_models.PackageParameter
class AdvancedComponentSearchForm(forms.Form): class AdvancedComponentSearchForm(forms.Form):
name = forms.CharField(max_length=255, label='Component Name', required=False) name = forms.CharField(max_length=255, label='Component Name', required=False)
package = AutocompleteForeingKeyField(required=False, api_search_url='package-list', foreign_model=parts_models.Package) package = AutocompleteForeingKeyField(required=False, api_search_url='package-list', foreign_model=parts_models.Package)
@ -313,7 +309,6 @@ class ComponentParameterSearchForm(forms.Form):
class ComponentParameterCreateForm(forms.Form): class ComponentParameterCreateForm(forms.Form):
parameter_type = AutocompleteForeingKeyField(required=True, foreign_model=parts_models.ComponentParameterType, api_search_url='componentparametertype-list', image_field_name=None, name_field_name='descriptive_name') parameter_type = AutocompleteForeingKeyField(required=True, foreign_model=parts_models.ComponentParameterType, api_search_url='componentparametertype-list', image_field_name=None, name_field_name='descriptive_name')
value = forms.CharField(required=True, max_length=256) value = forms.CharField(required=True, max_length=256)
model = parts_models.ComponentParameter
def clean(self): def clean(self):
data = super().clean() data = super().clean()
@ -343,20 +338,7 @@ class ComponentParameterCreateForm(forms.Form):
else: else:
text_value = '' text_value = ''
value = self.cleaned_data['number_value'] value = self.cleaned_data['number_value']
self.model.objects.create(parameter_type=param_type, component=component, value=value, text_value=text_value) parts_models.ComponentParameter.objects.create(parameter_type=param_type, component=component, value=value, text_value=text_value)
class PackageParameterCreateForm(ComponentParameterCreateForm):
model = parts_models.PackageParameter
def save(self, package):
param_type = self.cleaned_data['parameter_type']
if param_type.parameter_type == 'F':
text_value = self.cleaned_data['value']
value = 0
else:
text_value = ''
value = self.cleaned_data['number_value']
self.model.objects.create(parameter_type=param_type, package=package, value=value, text_value=text_value)
class QrSearchForm(forms.Form): class QrSearchForm(forms.Form):
my_qr_validator = QrCodeValidator() my_qr_validator = QrCodeValidator()

View File

@ -1,37 +0,0 @@
from django.core.management.base import BaseCommand, CommandParser
from django.contrib.auth import get_user_model
from parts.models import Component, ComponentParameter, ComponentParameterType, PackageParameter, Package
class Command(BaseCommand):
help = "Remove component parameters, that are also set on the package with the same value"
def add_arguments(self, parser: CommandParser):
parser.add_argument('--dry-run',
help='Do not perform parameter deletion. Print only',
action='store_true')
def handle(self, *args, **options):
# Get all components with set packages. Ignore the ones without packages
all_comps = Component.objects.exclude(package__isnull=True)
for component in all_comps:
package_parameters = PackageParameter.objects.filter(package=component.package)
component_parameters = ComponentParameter.objects.filter(component=component)
package_param_ids = package_parameters.values_list('parameter_type_id', flat=True)
component_param_ids = component_parameters.values_list('parameter_type_id', flat=True)
self.stdout.write(f'Comp: {str(component)} Found {len(component_param_ids)} different parameters')
self.stdout.write(f'\tPackage: {str(component.package)} Found {len(package_param_ids)} different parameters')
commontypes = ctypes = ComponentParameterType.objects.filter(id__in=component_param_ids).filter(id__in=package_param_ids)
self.stdout.write(f'\tCommon parameter count: {len(commontypes)}')
# Check if values are the same when rendered as a string. This avoids float comparison problems
for common_type in commontypes:
s1 = package_parameters.filter(parameter_type=common_type).first().resolved_value_as_string()
comp_param = component_parameters.filter(parameter_type=common_type).first()
s2 = comp_param.resolved_value_as_string()
if s1 == s2:
self.stdout.write(f'\tParameter {common_type.parameter_name} is the same value for component and package: {s1}. Removing from component')
if not options['dry_run']:
comp_param.delete()

View File

@ -1,23 +0,0 @@
from django.core.management.base import BaseCommand, CommandParser
from django.contrib.auth import get_user_model
class Command(BaseCommand):
help = "Create a default superuser if no superuser is already present. This aids automatic deployment inside a container."
def add_arguments(self, parser: CommandParser):
parser.add_argument('--user',
help='Username to create if no admin account is present',
default='admin')
parser.add_argument('--password',
help='Password to set for newly created user. Ignored, if any admin user is already present',
default='admin')
def handle(self, *args, **options):
User = get_user_model()
# Query if there is any admin user
if not User.objects.filter(is_superuser=True).exists():
self.stdout.write(f'No superuser present. Creating {options['user']} with supplied password')
User.objects.create_superuser(username=options['user'], password=options['password'])
else:
self.stdout.write('At least one superuser already exists. Skipping superuser creation')

View File

@ -8,7 +8,7 @@ from django.db.models import Q
from django.forms import formset_factory from django.forms import formset_factory
from django.db import IntegrityError from django.db import IntegrityError
from django.db.models import ProtectedError from django.db.models import ProtectedError
from ..models import Stock, Component, ComponentParameter, DistributorNum, PackageParameter from ..models import Stock, Component, ComponentParameter, DistributorNum
from ..forms import * from ..forms import *
from .component_import import import_components_from_csv from .component_import import import_components_from_csv
from .generic_views import BaseTemplateMixin from .generic_views import BaseTemplateMixin
@ -174,8 +174,6 @@ class ComponentDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
'distributor__name') 'distributor__name')
context['parameters'] = ComponentParameter.objects.filter(component=self.object).order_by( context['parameters'] = ComponentParameter.objects.filter(component=self.object).order_by(
'parameter_type__parameter_name') 'parameter_type__parameter_name')
context['package_parameters'] = PackageParameter.objects.filter(package=self.object.package).order_by(
'parameter_type__parameter_name')
return context return context

View File

@ -5,7 +5,7 @@ from django.core.paginator import Paginator
from django.db.models import ProtectedError from django.db.models import ProtectedError
from django.db.models import Q from django.db.models import Q
from ..forms import * from ..forms import *
from ..models import Package, PackageParameter from ..models import Package
from .generic_views import BaseTemplateMixin from .generic_views import BaseTemplateMixin
class PackageView(LoginRequiredMixin, BaseTemplateMixin, TemplateView): class PackageView(LoginRequiredMixin, BaseTemplateMixin, TemplateView):
@ -82,9 +82,6 @@ class PackageDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['package'] = self.object context['package'] = self.object
context['edit_form'] = PackageForm(instance=self.object) context['edit_form'] = PackageForm(instance=self.object)
context['new_param_form'] = PackageParameterCreateForm()
context['parameters'] = PackageParameter.objects.filter(package=self.object).order_by(
'parameter_type__parameter_name')
return context return context
@ -120,27 +117,6 @@ class PackageDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
context['edit_form'] = edit_form context['edit_form'] = edit_form
return self.render_to_response(context) return self.render_to_response(context)
def handle_submit_delete_param_post(self, request, **kwargs):
form = PackageParameterDeleteForm(data=request.POST)
if form.is_valid():
form.cleaned_data['param_num'].delete()
context = self.get_context_data(**kwargs)
return self.render_to_response(context)
def handle_submit_new_param_post(self, request, **kwargs):
form = PackageParameterCreateForm(data=request.POST)
if form.is_valid():
try:
form.save(self.object)
except IntegrityError:
form.add_error('__all__', 'This parameter is already set')
context = self.get_context_data(**kwargs)
if not form.is_valid():
context['new_param_form'] = form
return self.render_to_response(context)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
@ -148,9 +124,5 @@ class PackageDetailView(LoginRequiredMixin, BaseTemplateMixin, DetailView):
return self.handle_delete_package(request) return self.handle_delete_package(request)
elif 'submit-pkg-edit' in request.POST: elif 'submit-pkg-edit' in request.POST:
return self.edit_package(request) return self.edit_package(request)
elif 'submit-delete-param' in request.POST:
return self.handle_submit_delete_param_post(request, **kwargs)
elif 'submit-create-new-param' in request.POST:
return self.handle_submit_new_param_post(request, **kwargs)
return super().post(request, *args, **kwargs) return super().post(request, *args, **kwargs)

View File

@ -56,6 +56,7 @@ if get_env_value('DJANGO_FORCE_DEV_MODE', default=False) == 'True':
ALLOWED_HOSTS = ['127.0.0.1', 'localhost', get_env_value('DJANGO_ALLOWED_HOST')] ALLOWED_HOSTS = ['127.0.0.1', 'localhost', get_env_value('DJANGO_ALLOWED_HOST')]
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
@ -238,7 +239,4 @@ CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = False SECURE_SSL_REDIRECT = False
# allow detection of https behind "old" nginx
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_HSTS_SECONDS = get_env_value('DJANGO_SECURE_HSTS_SECONDS', default=120) SECURE_HSTS_SECONDS = get_env_value('DJANGO_SECURE_HSTS_SECONDS', default=120)

View File

@ -75,7 +75,7 @@
'component-parameter-type-list': '{% url 'componentparametertype-list' %}', 'component-parameter-type-list': '{% url 'componentparametertype-list' %}',
}; };
</script> </script>
<script type="text/javascript" src="{% static 'js/kenkyusho-api-v1.js' %}"></script> <script type="text/javascript" src="{% static 'js/kenyusho-api-v1.js' %}"></script>
<script type="text/javascript" src="{% static 'js/autocomplete.js' %}"></script> <script type="text/javascript" src="{% static 'js/autocomplete.js' %}"></script>
<script type="text/javascript" src="{% static 'js/autocomplete-foreign-key-field.js' %}"></script> <script type="text/javascript" src="{% static 'js/autocomplete-foreign-key-field.js' %}"></script>
<!-- Initialize bootstrap popovers --> <!-- Initialize bootstrap popovers -->

View File

@ -119,19 +119,6 @@
<th scope="col"></th> <th scope="col"></th>
</thead> </thead>
<tbody> <tbody>
{% for param in package_parameters %}
<tr>
<td>
<h6 {% if param.parameter_type.parameter_description %} class="accordion-header" data-bs-toggle="collapse" data-bs-target="#collapse-pkg-parameter-desc-{{forloop.counter}}"{% endif %}>
{{param.parameter_type.parameter_name}}
</h6>
</td>
<td>
{{param.resolved_value_as_string}}
</td>
<td><span class="text-info">from Package</span></td>
</tr>
{% endfor %}
{% for param in parameters %} {% for param in parameters %}
<tr> <tr>
<td> <td>
@ -161,13 +148,6 @@
</div> </div>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% for param in package_parameters %}
{% if param.parameter_type.parameter_description %}
<div class="collapse accordion-collapse" id="collapse-pkg-parameter-desc-{{forloop.counter}}" data-bs-parent="#accordion-param-desc">
{{param.parameter_type.parameter_description}}
</div>
{% endif %}
{% endfor %}
</div> </div>
</div> </div>
<div class="col"> <div class="col">

View File

@ -26,46 +26,6 @@
<input type="submit" class="btn btn-primary" value="Save" name="submit-pkg-edit"> <input type="submit" class="btn btn-primary" value="Save" name="submit-pkg-edit">
</form> </form>
</div> </div>
<div class="col-md-3">
<h3>Parameters <button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#new-component-parameter-modal"><i class="bi bi-plus-circle"></i></button></h3>
<table class="table align-middle mb-3">
<thead>
<th scope="col">Parameter</th>
<th scope="col">Value</th>
<th scope="col"></th>
</thead>
<tbody>
{% for param in parameters %}
<tr>
<td>
<h6 {% if param.parameter_type.parameter_description %} class="accordion-header" data-bs-toggle="collapse" data-bs-target="#collapse-parameter-desc-{{forloop.counter}}"{% endif %}>
{{param.parameter_type.parameter_name}}
</h6>
</td>
<td>
{{param.resolved_value_as_string}}
</td>
<td>
<form method="post">
{% csrf_token %}
<input type="hidden" value="{{param.id}}" name="param_num">
<button class="btn btn-danger" name="submit-delete-param">X</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="accordion" id="accordion-param-desc">
{% for param in parameters %}
{% if param.parameter_type.parameter_description %}
<div class="collapse accordion-collapse" id="collapse-parameter-desc-{{forloop.counter}}" data-bs-parent="#accordion-param-desc">
{{param.parameter_type.parameter_description}}
</div>
{% endif %}
{% endfor %}
</div>
</div>
</div> </div>
</div> </div>
@ -120,7 +80,7 @@
</div> </div>
</div> </div>
</div> </div>
{% include 'parts/modals/new-component-parameter-modal.html' with component_name=object.name form=new_param_form %}
{% endblock content %} {% endblock content %}

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
# Startup the db container # Startup the db container
docker compose start shimatta-kenkyusho-db docker-compose start shimatta-kenkyusho-db
# Override entrypoint to get interactive shell # Override entrypoint to get interactive shell
docker compose run --entrypoint="/bin/sh" -p 8000:8000 shimatta-kenkyusho-web docker-compose run --entrypoint="/bin/sh" -p 8000:8000 shimatta-kenkyusho-web