1 /** 
2  * Scheduling-related definitions
3  */
4 module eskomcalendar.schedule;
5 
6 import std.datetime.systime : SysTime;
7 import std.json : JSONValue, JSONException;
8 import std.conv : to;
9 import eskomcalendar.exceptions;
10 
11 /** 
12  * Represents a schedule for a given area,
13  * this includes the stage and the start
14  * and end time
15  */
16 public class Schedule
17 {
18     /** 
19      * Area this schedule applies to
20      */
21     private string area;
22 
23     /** 
24      * The stage level
25      */
26     private ubyte stage;
27 
28     /** 
29      * Start and finish time
30      */
31     private SysTime start, finish;
32     
33     /** 
34      * The source this schedule was gleamed from
35      */
36     private string source;
37 
38     /** 
39      * Private constructor to disallow creation except
40      * via the static factory method
41      */
42     private this()
43     {
44 
45     }
46 
47     /** 
48      * Get's the name of the area this schedule is for
49      *
50      * Returns: the area's name
51      */
52     public string getArea()
53     {
54         return area;
55     }
56 
57     /**
58      * Returns the load shedding stage level
59      *
60      * Returns: the stage
61      */
62     public ubyte getStage()
63     {
64         return stage;
65     }
66 
67     /** 
68      * Returns the starting time
69      *
70      * Returns: the starting time as a `SysTime`
71      */
72     public SysTime getStart()
73     {
74         return start;
75     }
76 
77     /** 
78      * Returns the ending time
79      *
80      * Returns: the starting time as a `SysTime`
81      */
82     public SysTime getFinish()
83     {
84         return finish;
85     }
86 
87     /** 
88      * Returns the source this schedule was gleamed from
89      *
90      * Returns: the source
91      */
92     public string getSource()
93     {
94         return source;
95     }
96 
97     /** 
98      * Constructs a new `Schedule` from the provided JSON
99      *
100      * Params:
101      *   json = the json to parse the schedule from
102      * Returns: the parsed `Schedule`
103      */
104     public static Schedule fromJSON(JSONValue json)
105     {
106         Schedule schedule = new Schedule();
107 
108         try
109         {
110             schedule.area = json["area_name"].str();
111             schedule.stage = cast(ubyte)(json["stage"].integer());
112             schedule.start = SysTime.fromISOExtString(json["start"].str());
113             schedule.finish = SysTime.fromISOExtString(json["finsh"].str());
114             schedule.source = json["source"].str();
115         }
116         catch(JSONException e)
117         {
118             throw new EskomCalendarException(ErrType.INVALID_SCHEDULE_DATA);
119         }
120 
121         return schedule;
122     }
123 
124     /** 
125      * Returns a string representation of the schedule
126      *
127      * Returns: a `string` representation
128      */
129     public override string toString()
130     {
131         return "Schedule [area: "~area~", stage: "~to!(string)(stage)~", from: "~start.toLocalTime().toSimpleString()~", to: "~finish.toLocalTime().toSimpleString()~"]";
132     }
133 }
134 
135 version(unittest)
136 {
137     import std.json : parseJSON;
138 }
139 
140 /**
141  * Test building a `Schedule` from the example JSON
142  */
143 unittest
144 {
145     string json = `
146     {
147     "area_name": "western-cape-worscester",
148     "stage": 4,
149     "start": "2023-06-01T14:00:00+02:00",
150     "finsh": "2023-06-01T14:30:00+02:00",
151     "source": "https://twitter.com/Eskom_SA/status/1664250326818365440"
152   }`;
153 
154     try
155     {
156         Schedule schedule = Schedule.fromJSON(parseJSON(json));
157 
158         assert(schedule.getArea() == "western-cape-worscester");
159         assert(schedule.getStage() == 4);
160         assert(schedule.getStart() == SysTime.fromISOExtString("2023-06-01T14:00:00+02:00"));
161         assert(schedule.getFinish() == SysTime.fromISOExtString("2023-06-01T14:30:00+02:00"));
162         assert(schedule.getSource() == "https://twitter.com/Eskom_SA/status/1664250326818365440");        
163     }
164     catch(EskomCalendarException e)
165     {
166         assert(false);
167     }
168 }
169 
170 /**
171  * Test building a `Schedule` from the example JSON
172  * buit where the JSON is BROKEN
173  */
174 private unittest
175 {
176     string json = `
177     {
178     "area_name": "western-cape-worscester",
179     "stage": 4,
180     "start": "2023-06-01T14:00:00+02:00",
181     "finsh": 2
182   }`;
183 
184     try
185     {
186         Schedule schedule = Schedule.fromJSON(parseJSON(json));
187 
188         assert(false);       
189     }
190     catch(EskomCalendarException e)
191     {
192         assert(e.getError() == ErrType.INVALID_SCHEDULE_DATA);
193     }
194 }