1
+
2
+
3
+ import json
4
+
5
+ from django import http
6
+ from django .views .decorators .http import require_POST
7
+ from specifyweb .specify .api import get_model
8
+
9
+ from specifyweb .specify .uiformatters import get_uiformatter_by_name
10
+ from specifyweb .specify .views import login_maybe_required
11
+
12
+ @login_maybe_required
13
+ @require_POST
14
+ def series_autonumber_range (request : http .HttpRequest ):
15
+ """
16
+ Returns a list of autonumbered values given a range.
17
+ Used for series data entry on Collection Objects.
18
+ """
19
+ request_data : dict = json .loads (request .body )
20
+ range_start = request_data .get ('rangestart' )
21
+ range_end = request_data .get ('rangeend' )
22
+ table_name = request_data .get ('tablename' )
23
+ field_name = request_data .get ('fieldname' )
24
+ formatter_name = request_data .get ('formattername' )
25
+
26
+ formatter = get_uiformatter_by_name (request .specify_collection , request .specify_user , formatter_name )
27
+
28
+ try :
29
+ range_start_parsed = formatter .parse (range_start )
30
+ assert not formatter .needs_autonumber (range_start_parsed )
31
+ canonicalized_range_start = formatter .canonicalize (range_start_parsed )
32
+ except :
33
+ return http .HttpResponseBadRequest ('Range start does not match format.' )
34
+ try :
35
+ range_end_parsed = formatter .parse (range_end )
36
+ assert not formatter .needs_autonumber (range_end_parsed )
37
+ canonicalized_range_end = formatter .canonicalize (range_end_parsed )
38
+ except :
39
+ return http .HttpResponseBadRequest ('Range end does not match format.' )
40
+
41
+ if canonicalized_range_end <= canonicalized_range_start :
42
+ return http .HttpResponseBadRequest (f'Range end must be greater than range start.' )
43
+
44
+ try :
45
+ # Repeatedly autonumber until the end is reached.
46
+ limit = 500
47
+ values = [canonicalized_range_start ]
48
+ current_value = values [0 ]
49
+ if request_data .get ('skipstartnumber' ):
50
+ # The first value can be optionally excluded/skipped.
51
+ # Needed since series entry currently relies on the first record being saved first.
52
+ values = []
53
+ while current_value < canonicalized_range_end :
54
+ current_value = '' .join (formatter .fill_vals_after (current_value ))
55
+ values .append (current_value )
56
+ if len (values ) >= limit :
57
+ return http .JsonResponse ({
58
+ 'values' : [],
59
+ 'error' : 'LimitExceeded' ,
60
+ })
61
+
62
+ # Check if any existing records use the values.
63
+ # Not garanteed to be accurate at the time of saving, just serves as a warning for the frontend.
64
+ table = get_model (table_name )
65
+ existing_records = table .objects .filter (** {f'{ field_name } __in' : values , 'collection' : request .specify_collection })
66
+ existing_values = list (existing_records .values_list (field_name , flat = True ))
67
+
68
+ if len (existing_values ) > 0 :
69
+ return http .JsonResponse ({
70
+ 'values' : values ,
71
+ 'existing' : existing_values ,
72
+ 'error' : 'ExistingNumbers' ,
73
+ })
74
+
75
+ return http .JsonResponse ({
76
+ 'values' : values ,
77
+ })
78
+ except Exception as e :
79
+ return http .JsonResponse ({'error' : 'An internal server error occurred.' }, status = 500 )
0 commit comments