Skip to content

Commit

Permalink
Merge pull request #2578 from SasView/moving_to_sas
Browse files Browse the repository at this point in the history
Webfit in parallel to qtGUI
  • Loading branch information
krzywon authored Dec 19, 2023
2 parents 6c4f13e + 0ebd214 commit 296221b
Show file tree
Hide file tree
Showing 46 changed files with 1,758 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ default_categories.json
**/UI/*.py
!**/UI/__init__.py
config.json
**/db.sqlite3

# doc build
/docs/sphinx-docs/build
Expand Down
Empty file added src/sas/webfit/__init__.py
Empty file.
Empty file.
3 changes: 3 additions & 0 deletions src/sas/webfit/analyze/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
6 changes: 6 additions & 0 deletions src/sas/webfit/analyze/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class DataConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "analyze"
Empty file.
4 changes: 4 additions & 0 deletions src/sas/webfit/analyze/fitting/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from django.contrib import admin
from .models import Fit, FitParameter

# Register your models here.
6 changes: 6 additions & 0 deletions src/sas/webfit/analyze/fitting/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class FittingConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "analyze.fitting"
40 changes: 40 additions & 0 deletions src/sas/webfit/analyze/fitting/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Generated by Django 4.2.2 on 2023-08-10 22:02

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

initial = True

dependencies = [
('analyze', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='Fit',
fields=[
('analysisbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='analyze.analysisbase')),
('results', models.CharField(blank=True, help_text='the string result', max_length=1000000, null=True)),
('status', models.IntegerField(choices=[(1, 'Queued'), (2, 'Running'), (3, 'Complete')], default=1)),
('q_minimum', models.FloatField(blank=True, default=0.0005, help_text='Minimum Q value for the fit', null=True)),
('q_maximum', models.FloatField(blank=True, default=0.5, help_text='Maximum Q value for the fit', null=True)),
('polydispersity', models.BooleanField(default=False, help_text='Is polydispersity being checked in this model')),
('magnetism', models.BooleanField(default=False, help_text='Is magnetism being checked in this model?')),
('model', models.CharField(help_text='model string', max_length=256)),
('optimizer', models.CharField(blank=True, help_text='optimizer string', max_length=50)),
],
bases=('analyze.analysisbase',),
),
migrations.CreateModel(
name='FitParameter',
fields=[
('analysisparameterbase_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='analyze.analysisparameterbase')),
('polydisperse', models.BooleanField(default=False, help_text='Is this a polydisperse parameter?')),
('magnetic', models.BooleanField(default=False, help_text='is this a magnetic parameter?')),
],
bases=('analyze.analysisparameterbase',),
),
]
Empty file.
45 changes: 45 additions & 0 deletions src/sas/webfit/analyze/fitting/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from logging import getLogger

from django.db import models

from ..models import AnalysisBase, AnalysisParameterBase

models_logger = getLogger(__name__)

class Fit(AnalysisBase):
results = models.CharField(max_length=1000000, blank=True,
null=True, help_text="the string result")
results_trace = [
]

class StatusChoices(models.IntegerChoices):
QUEUED = 1, "Queued"
RUNNING = 2, "Running"
COMPLETE = 3, "Complete"

status = models.IntegerField(default=StatusChoices.QUEUED, choices=StatusChoices.choices)

q_minimum = models.FloatField(default=0.0005, null=True,
blank=True, help_text="Minimum Q value for the fit")
q_maximum = models.FloatField(default=0.5, null=True,
blank=True, help_text="Maximum Q value for the fit")

polydispersity = models.BooleanField(default=False,
help_text="Is polydispersity being checked in this model")

magnetism = models.BooleanField(default=False,
help_text="Is magnetism being checked in this model?")

model = models.CharField(blank=False, max_length=256,
help_text="model string")

optimizer = models.CharField(blank=True, max_length=50,
help_text="optimizer string")


class FitParameter(AnalysisParameterBase):
polydisperse = models.BooleanField(default=False,
help_text="Is this a polydisperse parameter?")

magnetic = models.BooleanField(default=False,
help_text="is this a magnetic parameter?")
148 changes: 148 additions & 0 deletions src/sas/webfit/analyze/fitting/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Create your tests here.
import shutil
import json

from django.conf import settings
from django.test import TestCase
from django.contrib.auth.models import User
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.test import APIRequestFactory, APIClient, force_authenticate, APITestCase

from sasmodels.data import empty_data1D
from sasdata.dataloader.loader import Loader
import numpy as np

from data.models import Data
from .models import (
Fit,
FitParameter
)
from .views import (
start,
start_fit,
)

factory = APIRequestFactory()

# Create your tests here.
class TestLists(APITestCase):
"""def setUp(self):
self.user = User.objects.create_user(username="testUser", password="secret", id = 2)
self.client = APIClient()
self.client.force_authenticate(user=self.user)"""

#working
def get_model_list(self):
request = self.client.post('/v1/analyze/fit/models/')
self.assertEqual(request.data, {"all models":['adsorbed_layer', 'barbell', 'bcc_paracrystal', 'be_polyelectrolyte', 'binary_hard_sphere', 'broad_peak', 'capped_cylinder', 'core_multi_shell', 'core_shell_bicelle', 'core_shell_bicelle_elliptical', 'core_shell_bicelle_elliptical_belt_rough', 'core_shell_cylinder', 'core_shell_ellipsoid', 'core_shell_parallelepiped', 'core_shell_sphere', 'correlation_length', 'cylinder', 'dab', 'ellipsoid', 'elliptical_cylinder', 'fcc_paracrystal', 'flexible_cylinder', 'flexible_cylinder_elliptical', 'fractal', 'fractal_core_shell', 'fuzzy_sphere', 'gauss_lorentz_gel', 'gaussian_peak', 'gel_fit', 'guinier', 'guinier_porod', 'hardsphere', 'hayter_msa', 'hollow_cylinder', 'hollow_rectangular_prism', 'hollow_rectangular_prism_thin_walls', 'lamellar', 'lamellar_hg', 'lamellar_hg_stack_caille', 'lamellar_stack_caille', 'lamellar_stack_paracrystal', 'line', 'linear_pearls', 'lorentz', 'mass_fractal', 'mass_surface_fractal', 'mono_gauss_coil', 'multilayer_vesicle', 'onion', 'parallelepiped', 'peak_lorentz', 'pearl_necklace', 'poly_gauss_coil', 'polymer_excl_volume', 'polymer_micelle', 'porod', 'power_law', 'pringle', 'raspberry', 'rectangular_prism', 'rpa', 'sc_paracrystal', 'sphere', 'spherical_sld', 'spinodal', 'squarewell', 'stacked_disks', 'star_polymer', 'stickyhardsphere', 'superball', 'surface_fractal', 'teubner_strey', 'triaxial_ellipsoid', 'two_lorentzian', 'two_power_law', 'unified_power_Rg', 'vesicle']})

#working
def get_model_list_category(self):
data = {
"category":"Cylinder"
}
request = self.client.post('/v1/analyze/fit/models/', data, format='json')
self.assertEqual(request.data, {"Cylinder models":[['barbell', True], ['capped_cylinder', True], ['core_shell_bicelle', True], ['core_shell_bicelle_elliptical', True], ['core_shell_bicelle_elliptical_belt_rough', True], ['core_shell_cylinder', True], ['cylinder', True], ['elliptical_cylinder', True], ['flexible_cylinder', True], ['flexible_cylinder_elliptical', True], ['hollow_cylinder', True], ['pearl_necklace', True], ['pringle', True], ['stacked_disks', True]]})

#working
def get_model_list_kind(self):
data = {
"kind":"py"
}
request = self.client.post('/v1/analyze/fit/models/', data, format='json')
self.assertEqual(request.data, {"py models":['adsorbed_layer', 'be_polyelectrolyte', 'broad_peak', 'correlation_length', 'gauss_lorentz_gel', 'guinier_porod', 'line', 'peak_lorentz', 'poly_gauss_coil', 'polymer_excl_volume', 'porod', 'power_law', 'spinodal', 'teubner_strey', 'two_lorentzian', 'two_power_law', 'unified_power_Rg']})

def get_optimizer_list(self):
request = self.client.get('/v1/analyze/fit/optimizers/')
self.assertEqual(request.data, {"optimizers": [['amoeba', 'de', 'dream', 'newton', 'scipy.leastsq', 'lm']]})


class TestFitStart(TestCase):
def setUp(self):
self.user = User.objects.create_user(username="testUser", password="secret", id = 1)
self.client = APIClient()
self.client.force_authenticate(user=self.user)

self.public_test_data = Data.objects.create(id = 2, file_name = "cyl_400_20.txt", is_public = True)
self.public_test_data.file.save("cyl_400_20.txt", open(r"../src/sas/example_data/1d_data/cyl_400_20.txt"))

#self.private_test_data = Data.objects.create(id = 3, current_user = self.user, file_name = "another.txt", is_public = False)
#self.private_test_data.file.save("another.txt",open(r""))

"""self.fit = Fit.objects.create(id = 4, current_user = self.user, data_id = self.public_test_data, model = "cylinder", optimizer = "amoeba")
self.radius = FitParameter.objects.create(id = 5, base_id = self.fit, name = "radius", value = 35, data_type = "int", lower_limit = 1, upper_limit = 50)
self.length = FitParameter.objects.create(id = 6, base_id = self.fit, name = "length", value = 350, data_type = "int", lower_limit = 1, upper_limit = 500)
self.background = FitParameter.objects.create(id = 7, base_id = self.fit, name = "background", value = 0.0, data_type = "float")
self.scale = FitParameter.objects.create(id = 8, base_id = self.fit, name = "scale", value = 1.0, data_type = "float")
self.sld = FitParameter.objects.create(id = 9, base_id = self.fit, name = "sld", value = 4.0, data_type = "float")
self.sld_solvent = FitParameter.objects.create(id = 10, base_id = self.fit, name = "sld_solvent", value = 1.0, data_type = "float")
self.all_params = [self.radius, self.length, self.background, self.scale, self.sld, self.sld_solvent]"""

def test_can_fit_start_give_correct_answer(self):
data = Data.objects.get(is_public = True)
chisq = start_fit(fit_db=self.fit, par_dbs=self.all_params)
self.assertEqual(chisq,"0.03(13)")

def test_can_fit_give_right_answer(self):
data = {
"model":"cylinder",
"data_id":2,
"optimizer":"amoeba",
"parameters" :
[
{
"name":"radius",
"value":35,
"data_type":"int",
"lower_limit":1,
"upper_limit":50
},
{
"name":"length",
"value":350,
"data_type":"int",
"lower_limit":1,
"upper_limit":500
},
{
"name":"background",
"value":0.0,
"data_type":"float"
},
{
"name":"scale",
"value":1.0,
"data_type":"float"
},
{
"name":"sld",
"value":4.0,
"data_type":"float"
},
{
"name":"sld_solvent",
"value":1.0,
"data_type":"float"
}
]
}
request = self.client.post('/v1/analyze/fit/', data=json.dumps(data), content_type="application/json")
self.assertEqual(request.data,{'authenticated': True, 'fit_id': 1, 'results': '0.03(13)'})

def tearDown(self):
shutil.rmtree(settings.MEDIA_ROOT)

class TestLoader(TestCase):

def testing_loader(self):
test_data = get_object_or_404(Data, is_public=True)
loader = Loader()
loader.load(test_data.file.path)

def testing_syntax(self):
test_data = Data.objects.create(id = 2, is_public = True)
self.assertEqual("cyl_400_20.txt",test_data.current_user)

def tearDown(self):
shutil.rmtree(settings.MEDIA_ROOT)
29 changes: 29 additions & 0 deletions src/sas/webfit/analyze/fitting/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import logging
from django.urls import path, re_path, include
from . import views

logger = logging.getLogger(__name__)

#info urls
info_patterns = [
path("", views.fit_info),
path("status/", views.status, name = "get status using fit_id"),
path("parameters/", views.view_parameters, name = "current parameters"),
#TODO impliment parameter trace
#path("parameter_history/"),
#TODO allow user to find specific parts of results
path("results/", views.view_results, name = "current results"),
#TODO impliment results trace
#path("results_history/")
]

#urls to fit
urlpatterns = [
path("", views.start, name = "starting a fit"),
path("status/", views.status, name = "get status of all user's fits"),
path("<int:fit_id>/", include(info_patterns), name = "get fit info"),
path("optimizers/", views.list_optimizers, name = "lists all fit optimizers"),
path("models/", views.list_model, name = "lists all fit models"),
path("models/<str:category>/", views.list_model, name = "lists all fit models"),
path("models/<str:kind>/", views.list_model, name = "lists all fit models"),
]
Loading

0 comments on commit 296221b

Please sign in to comment.