1
00:00:00,000 --> 00:00:17,360
I just want to thank you. You've done an outstanding job with the instrument import.

2
00:00:18,880 --> 00:00:19,840
Thanks. Thanks.

3
00:00:22,160 --> 00:00:28,320
Oops. There we go. Good morning, Nick.

4
00:00:28,320 --> 00:00:29,520
Morning. Hi, Nick.

5
00:00:29,520 --> 00:00:39,760
Hey. Well, just to kick it off this morning, I just sort of want to spotlight some of the work Charles has done. So,

6
00:00:41,040 --> 00:00:44,240
do you want to tell us about the import real quick, Charles? And then I can

7
00:00:46,240 --> 00:00:47,520
pull it up on the screen.

8
00:00:47,520 --> 00:00:59,120
Yeah, so it's this pandas code that reads in a text file from this

9
00:00:59,120 --> 00:01:12,240
a text file from this Shimazu Nexera. And it just sort of cleans up the data and the column names and

10
00:01:13,280 --> 00:01:18,480
puts it into a readable pandas or readable pandas data frame.

11
00:01:18,480 --> 00:01:27,440
Exactly. So, Nick, so essentially at the laboratories, when

12
00:01:30,320 --> 00:01:36,240
when the data comes off the instrument, it essentially just looks like this.

13
00:01:37,520 --> 00:01:45,600
So, this is what the scientific instrument is spitting out whenever you send in the test result.

14
00:01:45,600 --> 00:01:52,800
So, here it's just may look like a bunch of godly gook at first, but you'll see, you know, we've got

15
00:01:52,800 --> 00:02:02,880
CBDV, CBDA, CBGA. So, this is what the instrument's actually measuring,

16
00:02:04,400 --> 00:02:14,080
and we're getting a lot of readings, but primarily what we're looking for in all of this mess

17
00:02:14,080 --> 00:02:18,240
is the concentration. So,

18
00:02:20,400 --> 00:02:26,400
there's a lot of data, and we really just need one data point. Well, we need a couple data points, but

19
00:02:29,920 --> 00:02:37,520
so this is where data wranglers like Charles play a critical role. And so

20
00:02:37,520 --> 00:02:42,000
Charles has written

21
00:02:42,560 --> 00:02:50,400
this import method. So, this import NXSERA, and so essentially,

22
00:02:52,560 --> 00:03:01,440
you know, all you have to do is point to that data file.txt

23
00:03:01,440 --> 00:03:09,440
and

24
00:03:09,840 --> 00:03:22,080
we now have that data in the slightly cleaner format. So, we now have the compound, and so, for example,

25
00:03:22,080 --> 00:03:30,160
if you look at the last observation,

26
00:03:37,200 --> 00:03:42,160
well, it looks like there's a lot of columns, so let's iterate over this real quick. So,

27
00:03:42,160 --> 00:03:53,040
did you want me to drop some of those columns? Well, a principle that the Craig Demkin, Dr. Craig

28
00:03:53,040 --> 00:04:00,640
Demkin, he's an economics professor at UNC Charlotte, he stressed to me, never throw away data,

29
00:04:01,680 --> 00:04:08,640
and it's not a good idea to throw away data. So, he's not going to be able to do that.

30
00:04:08,640 --> 00:04:15,680
So, he's going to throw away data, and I mean, to a certain extent, you know, you need to clean up the data,

31
00:04:15,680 --> 00:04:22,000
but it's a good principle to keep in mind, and his observation is, you know,

32
00:04:23,360 --> 00:04:28,960
especially when you start to take means and things, you know, you start to, you know, you start to,

33
00:04:30,880 --> 00:04:37,120
you know, lose some of the data, and you know, you don't want to just be throwing away variables and

34
00:04:37,120 --> 00:04:45,680
portions of the data willy-nilly. So, you know, as data storage becomes cheaper and cheaper,

35
00:04:47,440 --> 00:04:51,520
my philosophy is just archive everything, so just save it.

36
00:04:53,680 --> 00:04:59,040
There are, here, so let's try to print out all these variables here, and then we can just talk

37
00:04:59,040 --> 00:05:08,320
about the things that are interesting. So, I'm not 100% sure how we can iterate over

38
00:05:08,320 --> 00:05:30,320
this one observation, but we can try a couple things. Okay.

39
00:05:38,320 --> 00:05:48,320
So, I kind of want to print out this whole thing, but it's a little long. I think.

40
00:05:48,320 --> 00:06:02,320
Okay, so now we can see, so this is essentially one observation. So, this is one compound of

41
00:06:02,320 --> 00:06:16,320
of one analysis of one sample, and so we've got all of these data points for this one compound. So,

42
00:06:16,320 --> 00:06:30,320
128 data points. So, that's probably excessive, and as you see, a lot of these are just blank,

43
00:06:30,320 --> 00:06:38,320
but I'm going to try to get a little bit more of a sense of what's going on here. So,

44
00:06:38,320 --> 00:06:46,320
the things that you do need for certain, you need to know the compound. It helps the analyst and

45
00:06:46,320 --> 00:06:54,320
the software to know where the data is stored on the instrument. So, this is the actual file path

46
00:06:55,040 --> 00:07:02,320
on the instrument. So, this is the actual data that's stored on the instrument. So,

47
00:07:02,320 --> 00:07:10,320
this is the actual file path on the instrument. So, scientific instruments are essentially wired up

48
00:07:10,320 --> 00:07:18,320
or wireless to the computer, and so the data is typically stored on that computer.

49
00:07:20,320 --> 00:07:28,320
So, then you also want to know the sample name, the sample ID, depending on the instrument,

50
00:07:28,320 --> 00:07:40,320
then the sample type, if the analyst has entered it, the retention time. So, just a brief,

51
00:07:40,320 --> 00:08:00,320
brief, very brief, so just a brief

52
00:08:00,320 --> 00:08:12,320
introduction to a chromatograph. So, essentially, you're running the analysis on an HPLC or

53
00:08:12,320 --> 00:08:22,320
another scientific instrument that does chromatography, and

54
00:08:22,320 --> 00:08:28,320
so your, say, runtime may be maybe on a faster machine, maybe, or a faster instrument, maybe

55
00:08:28,320 --> 00:08:34,320
it could be as low as four to seven minutes, but maybe, you know, a typical would be 11 to 16 minutes

56
00:08:34,320 --> 00:08:42,320
per analysis for a single sample. So, you're going to have to do a little bit of a run time

57
00:08:42,320 --> 00:08:52,320
to get that done. So, essentially, you're going to be passing

58
00:08:54,320 --> 00:09:02,320
little injections of the sample through the instrument, and

59
00:09:02,320 --> 00:09:12,320
if something's detected through chromatography, you'll get a response, and then the area of the peak

60
00:09:12,320 --> 00:09:22,320
represents the amount, sort of the concentration of a particular substance in that injection.

61
00:09:22,320 --> 00:09:32,320
How do you know what type of substance it is? Well,

62
00:09:34,320 --> 00:09:42,320
that is the retention time. So, here you're seeing the retention time of 6.323.

63
00:09:42,320 --> 00:09:52,320
So, let's just conjecture that this is six minutes and 23 seconds.

64
00:09:52,320 --> 00:10:02,320
This is THC, and then what's been done is people have isolated THCA, and through repeated scientific studies,

65
00:10:02,320 --> 00:10:10,320
they've been able to detect the results of the test. So, you're going to have to do a little bit of a run time

66
00:10:10,320 --> 00:10:20,320
to get that done. So, in these scientific studies, they've determined that given your parameters,

67
00:10:20,320 --> 00:10:30,320
THCA should elude, so that's when it comes off your column and creates this peak, at around

68
00:10:30,320 --> 00:10:40,320
you can calibrate your instrument to essentially label this peak automatically as THCA.

69
00:10:40,320 --> 00:10:54,320
So, we've got that, and then the concentration, 49.445, is this area under the curve.

70
00:10:54,320 --> 00:11:04,320
And so, that represents the concentration of THCA in the sample.

71
00:11:04,320 --> 00:11:16,320
Oh, that was injected through the instrument. So, those are the main data points you're looking for.

72
00:11:16,320 --> 00:11:24,320
Of course, the chemists may be interested in some of the other variables. So, for example,

73
00:11:24,320 --> 00:11:34,320
they may be interested in when the peak started and when the peak ended, because you want a nice uniform peak.

74
00:11:34,320 --> 00:11:44,320
You don't want lumpy peaks, so to speak.

75
00:11:44,320 --> 00:11:54,320
And a chemist could probably tell you a lot more about more of these variables than I.

76
00:11:54,320 --> 00:12:04,320
And so, that's why I just resort back to the philosophy of never throw away data.

77
00:12:04,320 --> 00:12:12,320
And so, may as well save it in case someone needs it in the future.

78
00:12:12,320 --> 00:12:24,320
So, let's just sort of a quick scientific analysis just to give Charles props for turning this data,

79
00:12:24,320 --> 00:12:41,320
this text file into a reasonable data frame that we can now do work with.

80
00:12:41,320 --> 00:13:04,320
So, for example, we can find a particular sample.

81
00:13:04,320 --> 00:13:16,320
What was the variable's name again?

82
00:13:16,320 --> 00:13:28,320
And so, with the click of a button, you now have all of the compounds for this method.

83
00:13:28,320 --> 00:13:35,320
It looks like there could have been two runs of this.

84
00:13:35,320 --> 00:13:41,320
So, let's actually just find out all of our samples real quick. So, this is the beauty.

85
00:13:41,320 --> 00:13:49,320
So, we're just basically going to show what's so cool about the work Charles has done here.

86
00:13:49,320 --> 00:13:58,320
So, because it's all of this data is in a data frame, we can make short work of it.

87
00:13:58,320 --> 00:14:07,320
So, we can find the samples.

88
00:14:07,320 --> 00:14:13,320
So, say you were looking for the sample name QC-PREP-A.

89
00:14:13,320 --> 00:14:22,320
Well, now you have all of the cannabinoids that were analyzed.

90
00:14:22,320 --> 00:14:29,320
And you could take it from there.

91
00:14:29,320 --> 00:14:36,320
And so, essentially, this is a critical step in the data collection process because I'll just let you know,

92
00:14:36,320 --> 00:14:48,320
when I started as a laboratory analyst, essentially, until there was a good way to import this data,

93
00:14:48,320 --> 00:15:00,320
I was essentially just transcribing all of these concentrations by hand into the database.

94
00:15:00,320 --> 00:15:12,320
So, as you could imagine, you know, as you can imagine here,

95
00:15:12,320 --> 00:15:18,320
so for each of these, I would be transcribing the concentration.

96
00:15:18,320 --> 00:15:29,320
So, as you can see, it's easily quite a nightmare because you're having to do all of this crazy parsing

97
00:15:29,320 --> 00:15:33,320
and the number is just hidden right there.

98
00:15:33,320 --> 00:15:44,320
And then for one sample, you're having to do 11, in this case, 11 compounds.

99
00:15:44,320 --> 00:15:53,320
And so, you know, 11 times 3, I mean, that's 33 data points you have to enter for three samples.

100
00:15:53,320 --> 00:15:59,320
Or you could use Charles import method and just import all the data.

101
00:15:59,320 --> 00:16:03,320
It's ready to go. It's in the pandas data frame.

102
00:16:03,320 --> 00:16:14,320
And my philosophy with Python is if you can do it with a computer and you've got enough time and willpower and know how,

103
00:16:14,320 --> 00:16:16,320
then you can do it with Python.

104
00:16:16,320 --> 00:16:26,320
So, long story short, awesome work, Charles. Just phenomenal.

105
00:16:26,320 --> 00:16:30,320
Yeah, that's really cool.

106
00:16:30,320 --> 00:16:38,320
And Charles has actually pushed this to the, like, CamLinux engine.

107
00:16:38,320 --> 00:16:44,320
And so, Nick, this is sort of maybe something up your alley.

108
00:16:44,320 --> 00:16:52,320
So, this is essentially an interface for cannabis analytics.

109
00:16:52,320 --> 00:16:58,320
So, primarily started out geared towards laboratories.

110
00:16:58,320 --> 00:17:06,320
So, importing data and getting it into the traceability system and getting certificates to clients.

111
00:17:06,320 --> 00:17:12,320
However, it needed a good interface for the traceability system.

112
00:17:12,320 --> 00:17:27,320
So, I essentially wrote a wrapper around the metric API.

113
00:17:27,320 --> 00:17:50,320
And just to show you, it's nothing like fancy here. So, I essentially modeled this off of G spread.

114
00:17:50,320 --> 00:17:58,320
This is just a random Python package that wraps around the Google spreadsheets API.

115
00:17:58,320 --> 00:18:07,320
And I thought it was a good example of an API wrapper.

116
00:18:07,320 --> 00:18:14,320
So, I modeled the metric API wrapper around G spread.

117
00:18:14,320 --> 00:18:27,320
So, it's got a similar structure, but essentially it just leverages requests to hit the metric API.

118
00:18:27,320 --> 00:18:30,320
So, just sort of...

119
00:18:30,320 --> 00:18:33,320
So, Nick, real quick question.

120
00:18:33,320 --> 00:18:48,320
As, say, a licensee in Oregon, are you allowed to use the metric API or do you have to go through a third-party vendor?

121
00:18:48,320 --> 00:18:50,320
We can use metric.

122
00:18:50,320 --> 00:18:57,320
Excellent. So, hopefully, this is a tool that you could just use right out of the box.

123
00:18:57,320 --> 00:19:04,320
So, centrally, you'd authorize your metric client.

124
00:19:04,320 --> 00:19:12,320
So, this just creates an instance of this client here.

125
00:19:12,320 --> 00:19:14,320
And there's nothing really fancy going on.

126
00:19:14,320 --> 00:19:28,320
It's just a class, and it just keeps your credentials as a header. So, it just creates a session, you know,

127
00:19:28,320 --> 00:19:34,320
sets your vendor API key and user API key as the authorization.

128
00:19:34,320 --> 00:19:44,320
Because I'm getting a sneaking suspicion that metric may use Python as their backend for their API.

129
00:19:44,320 --> 00:19:52,320
Because they may not, but some of the responses I get are...

130
00:19:52,320 --> 00:20:00,320
I'm not sure. Just sort of the way it's structured and just the way it plays so nicely with requests.

131
00:20:00,320 --> 00:20:07,320
I don't know. So, it may not be, but it does play real nicely with requests.

132
00:20:07,320 --> 00:20:18,320
So, all you have to do is set the authorization on your session with essentially...

133
00:20:18,320 --> 00:20:26,320
So, this is essentially your username and password.

134
00:20:26,320 --> 00:20:30,320
So, it's just sort of brilliantly simple.

135
00:20:30,320 --> 00:20:38,320
And then the client...

136
00:20:38,320 --> 00:20:40,320
You can interact with it in a couple ways.

137
00:20:40,320 --> 00:20:49,320
So, you can either use the client to just, you know, do all your workflow.

138
00:20:49,320 --> 00:20:59,320
Or the client actually returns to, for example, like if you're working with employees,

139
00:20:59,320 --> 00:21:07,320
it'll actually return an employee model.

140
00:21:07,320 --> 00:21:19,320
And so, if you want to sort of dig into the code, the models is a pretty informative place to begin.

141
00:21:19,320 --> 00:21:28,320
So, the base model just sort of sets up a lot of cool functionality that integrates with Firebase.

142
00:21:28,320 --> 00:21:42,320
But essentially, all of the metric models are represented here.

143
00:21:42,320 --> 00:21:49,320
So, we've got an employee. We've got a facility.

144
00:21:49,320 --> 00:21:56,320
So, the employee is a really simple model, just a full name license.

145
00:21:56,320 --> 00:22:01,320
Then you've got the facilities.

146
00:22:01,320 --> 00:22:15,320
And then, for example, this is how you're able to, you know, to leverage a lot of the functionality straight from the model.

147
00:22:15,320 --> 00:22:23,320
So, you've got facilities. You have locations.

148
00:22:23,320 --> 00:22:33,320
Strains. Items. Plants.

149
00:22:33,320 --> 00:22:38,320
So, just to give you an idea, we don't have to scroll through the whole thing, but harvests.

150
00:22:38,320 --> 00:22:48,320
So, you have packages. Patients. So, patience is on a state-by-state basis.

151
00:22:48,320 --> 00:22:51,320
So, I don't believe all states have patience.

152
00:22:51,320 --> 00:23:02,320
Nick, in Oregon, do you interact with, like, patients at all in the metric system, or is that just not really a requirement?

153
00:23:02,320 --> 00:23:09,320
No, because we don't sell to patients. We sell to the retailer.

154
00:23:09,320 --> 00:23:16,320
So, the retailer may, because we're on the manufacturing end.

155
00:23:16,320 --> 00:23:21,320
So, we don't sell to patients.

156
00:23:21,320 --> 00:23:24,320
All right. So, we know, yeah.

157
00:23:24,320 --> 00:23:29,320
So, but there's, and here are some interesting helpers for you.

158
00:23:29,320 --> 00:23:36,320
So, there's lab results, and then there's transfers.

159
00:23:36,320 --> 00:23:42,320
So, these models could really help you out.

160
00:23:42,320 --> 00:23:52,320
So, to give you an idea about how you would actually use some of the code, you can actually check out the tests.

161
00:23:52,320 --> 00:24:00,320
So, if you go to tests, traceability, metric test,

162
00:24:00,320 --> 00:24:13,320
this sort of walks through how you would actually use all of the endpoints, or at least the majority of the endpoints.

163
00:24:13,320 --> 00:24:25,320
So, just sort of to walk you through this real quick, in case you're interested in using it.

164
00:24:25,320 --> 00:24:35,320
The way I would recommend, you know, firing everything up is maybe, and you can have your own setup if you would like,

165
00:24:35,320 --> 00:24:43,320
but I would, I mean, you may have your API keys on your server,

166
00:24:43,320 --> 00:24:50,320
but in this case, I just have them in an environmental variable file, so a.env file.

167
00:24:50,320 --> 00:24:59,320
So, you can just read in your credentials from the.env file,

168
00:24:59,320 --> 00:25:08,320
and then you get your vendor API key and your user API key one way or the other, in a secure way,

169
00:25:08,320 --> 00:25:13,320
and then you can fire up a client.

170
00:25:13,320 --> 00:25:22,320
So, you can name it whatever you want, I just call it track, just for a nice short namespace.

171
00:25:22,320 --> 00:25:29,320
And then, once it's set up, you can get off to the races.

172
00:25:29,320 --> 00:25:42,320
So, you can get your facilities, and the reason that's important is you'll want to know the license number of your facility.

173
00:25:42,320 --> 00:25:55,320
So, this example, you'll notice here we're creating locations,

174
00:25:55,320 --> 00:26:00,320
and you'll notice that I actually pass a license number here.

175
00:26:00,320 --> 00:26:07,320
So, you can either pass a license number every time,

176
00:26:07,320 --> 00:26:16,320
if you have multiple licenses, and you need that flexibility,

177
00:26:16,320 --> 00:26:21,320
or if you just have a single license and you just want to simplify things,

178
00:26:21,320 --> 00:26:34,320
you can actually pass the client a primary.

179
00:26:34,320 --> 00:26:39,320
Exactly, so you can pass the client a primary license.

180
00:26:39,320 --> 00:26:56,320
So, that way you don't have to pass the license number every single time you're doing something.

181
00:26:56,320 --> 00:27:07,320
But in this example, I am passing the license number just to show you how you can use, say, a cultivator's workflow,

182
00:27:07,320 --> 00:27:12,320
and maybe a retailer's workflow down below.

183
00:27:12,320 --> 00:27:23,320
So, long story short, you may want to check it out because you can initialize this metric client,

184
00:27:23,320 --> 00:27:27,320
and then you can do everything under the sun with metric.

185
00:27:27,320 --> 00:27:34,320
You can create your locations, update them, view them.

186
00:27:34,320 --> 00:27:39,320
You can manage your strains.

187
00:27:39,320 --> 00:27:48,320
So, this is sort of how you go about creating strains.

188
00:27:48,320 --> 00:27:55,320
This is some functionality that may need to get worked into the actual client itself.

189
00:27:55,320 --> 00:28:04,320
It may need to be implemented at some point because essentially, when you create, so here, when you create a strain,

190
00:28:04,320 --> 00:28:17,320
in this case, I'm creating the new old-time moonshine, you're not returning the ID, unfortunately.

191
00:28:17,320 --> 00:28:28,320
So, you actually need to do a quick get call on the strains to actually get that new strain.

192
00:28:28,320 --> 00:28:46,320
And you'll notice that actually I'm not certain if you can filter strains by time.

193
00:28:46,320 --> 00:28:51,320
I may not be able to.

194
00:28:51,320 --> 00:28:58,320
But similar things with items.

195
00:28:58,320 --> 00:29:12,320
So, you can sort of use this test case as an example of how you would go about creating items or just getting items.

196
00:29:12,320 --> 00:29:21,320
Because before your manager gives you the green light just to start willy-nilly,

197
00:29:21,320 --> 00:29:27,320
well, you don't want to willy-nilly do anything, but before you get the green light to start posting,

198
00:29:27,320 --> 00:29:32,320
you can just start by just getting your inventory.

199
00:29:32,320 --> 00:29:49,320
So, just get your items, get your batches, and then, you know, I guess your processors, you may not have plants or harvests,

200
00:29:49,320 --> 00:29:55,320
but then you can get your packages. So, these will be critical for you.

201
00:29:55,320 --> 00:30:07,320
So, that way, you know, you can just go ahead and get your packages, and that way you can get them into Python and start analysis.

202
00:30:07,320 --> 00:30:17,320
So, you know, I'm sure you could put together some interesting.

203
00:30:17,320 --> 00:30:30,320
Yeah, I'm sure you could put together some interesting statistics that could impress your boss.

204
00:30:30,320 --> 00:30:48,320
And then finally, when you are ready to really ready to start with the workflow, then you can start doing transfers.

205
00:30:48,320 --> 00:30:57,320
So, transfers are a little complex. And so, I'm interested to see how this would work in production.

206
00:30:57,320 --> 00:31:09,320
So, if someone wants to start using the transfers in production, it would be quite interesting to see and maybe see how we could optimize it.

207
00:31:09,320 --> 00:31:21,320
But transfers are a bit more complex in metric than they are in leaf data systems.

208
00:31:21,320 --> 00:31:33,320
But it's approachable. So, essentially, what's happening here with the transfer is you've got your shipper.

209
00:31:33,320 --> 00:31:44,320
And so, that's, you know, that's the person, of course, transporting it.

210
00:31:44,320 --> 00:31:59,320
So, and then you, of course, need your recipient, that information about their license, and then you then, of course, add all the packages.

211
00:31:59,320 --> 00:32:07,320
So, they may need to be a bit more efficient way than just passing into giant object.

212
00:32:07,320 --> 00:32:13,320
But at the moment, that's the way transfers are created.

213
00:32:13,320 --> 00:32:35,320
However, once they're created, because of the nice models we have here, you can, you know, you can update the, you know, you can update the transfers, you know, without much trouble.

214
00:32:35,320 --> 00:32:46,320
So, you know, so this is sort of the tortoise and the hare philosophy where, you know, it took a little bit of time, you know, to write all those models.

215
00:32:46,320 --> 00:33:03,320
But now they sort of pay off because especially the transfer ones.

216
00:33:03,320 --> 00:33:15,320
So, basically, the majority of these classes have built in functions to create, update, and delete.

217
00:33:15,320 --> 00:33:33,320
So, almost all of the class, all of the models, for the most part, have those three. So, that way you can just create, update, and delete a model just from itself.

218
00:33:33,320 --> 00:33:45,320
Ah, yes, there was actually the client. So, the client has some helper functions to help out with transfers.

219
00:33:45,320 --> 00:34:03,320
So, I wish I could print these out real quick, but I'll start wrapping this up because long story short, there's a lot of, a lot of utility functions you can use here to make your life easier.

220
00:34:03,320 --> 00:34:10,320
You can get transfer types. You can get some, you can get all the transporters.

221
00:34:10,320 --> 00:34:15,320
You can get a transporter's details.

222
00:34:15,320 --> 00:34:22,320
And then, of course, you know, create, update, and delete.

223
00:34:22,320 --> 00:34:35,320
So, I just sort of wanted to just sort of show you that real quick. Just to, you know, just to sort of tell you about this tool because,

224
00:34:35,320 --> 00:34:42,320
you know, it's here, it's here if you need, if you want to use it, and it's designed to be simple and easy.

225
00:34:42,320 --> 00:34:55,320
So, ideally, you can just toss in your vendor API key and your user API key, and you're off to the races.

226
00:34:55,320 --> 00:35:11,320
So, like I said, it still needs, it still needs to be hammered a bit in production so that way we can figure out all of the interesting utility functions that can be added to it to make everybody's lives easier.

227
00:35:11,320 --> 00:35:17,320
But it does function. It does successfully hit all of the endpoints.

228
00:35:17,320 --> 00:35:33,320
So, it works. So, may as well put it out there and, you know, see if anybody can find it useful.

229
00:35:33,320 --> 00:35:50,320
So, I guess before we move on, I guess, are there any quick questions about the metric interface?

230
00:35:50,320 --> 00:35:53,320
No, you've done some really good work.

231
00:35:53,320 --> 00:36:04,320
Thanks. And like I said, it's inspired by Gspread. So, really, really from start to finish.

232
00:36:04,320 --> 00:36:21,320
So, I mean, so just to kind of show you some of the similarity here.

233
00:36:21,320 --> 00:36:31,320
So, you know, see they just have their client, their model, their URLs, exceptions, and some utility functions.

234
00:36:31,320 --> 00:36:48,320
And metric, same thing. I've got the client, some exceptions. So, just to handle the, you know, the base metric error, errors that are thrown.

235
00:36:48,320 --> 00:37:00,320
And then, oh yes, and then the URLs. And so, when you initialize the client, actually,

236
00:37:00,320 --> 00:37:06,320
sort of not shown in the demo, but you'll want to pass it your state.

237
00:37:06,320 --> 00:37:19,320
So, the state is actually by default at Oklahoma, because that's, that was just where, like, K-NLitics was verified first.

238
00:37:19,320 --> 00:37:31,320
However, so when you initialize your client, you'll want, when you initialize your client, you'll want to essentially use this snippet here.

239
00:37:31,320 --> 00:37:43,320
Because the one in the example is sort of the simple, like the most minimal case where you just pass the vendor API key and the user API.

240
00:37:43,320 --> 00:37:54,320
You'll probably also want to pass, you'll definitely want to pass the state equals to OR load case.

241
00:37:54,320 --> 00:38:05,320
And then you may want to pass the primary license if you're just thinking you're going to just work with one license.

242
00:38:05,320 --> 00:38:14,320
So, you know, nothing fancy there, just all the URLs.

243
00:38:14,320 --> 00:38:26,320
And then there's, you know, a handful of utility functions, which these are just sort of things that sort of suit my fancy.

244
00:38:26,320 --> 00:38:33,320
You know, for example, you know, just my preference for snake case. So, a lot of the package.

245
00:38:33,320 --> 00:38:45,320
To basically metric a lot of their fields or camel case. And I have a hard time reading camel case.

246
00:38:45,320 --> 00:38:51,320
I can read snake case faster. And so I just turn everything to snake case.

247
00:38:51,320 --> 00:38:57,320
So if that's not preferred, you know, just leave some feedback.

248
00:38:57,320 --> 00:39:05,320
I don't know. I feel like it makes things simpler. And then then it's all brought together here.

249
00:39:05,320 --> 00:39:12,320
So the user has to go in and sort of manually

250
00:39:12,320 --> 00:39:23,320
change all those fields or is there like a setup utility with like a user interface where you like walk them through what they need to set up?

251
00:39:23,320 --> 00:39:29,320
Good question, Charles. So essentially.

252
00:39:29,320 --> 00:39:36,320
This is designed to to maybe be plugged up to a user interface.

253
00:39:36,320 --> 00:39:42,320
So this is more of an API. So this is well, this is a wrapper around the API.

254
00:39:42,320 --> 00:39:51,320
So this is. This is essentially like a programming interface to the API.

255
00:39:51,320 --> 00:39:59,320
So just. Just to basically make the API easier to use through Python.

256
00:39:59,320 --> 00:40:07,320
But if you were essentially designing a user interface.

257
00:40:07,320 --> 00:40:15,320
You would. You would still currently need to essentially pass some of these parameters.

258
00:40:15,320 --> 00:40:22,320
So here actually the test case is a good example.

259
00:40:22,320 --> 00:40:28,320
So in the user interface, you'll you'll need to collect.

260
00:40:28,320 --> 00:40:34,320
You know, certain variables. So just to sort of show you. So, for example.

261
00:40:34,320 --> 00:40:43,320
For locations. So here to create a location.

262
00:40:43,320 --> 00:40:50,320
You need.

263
00:40:50,320 --> 00:40:57,320
Well, the locations I've sort of tried to make a bit more efficient.

264
00:40:57,320 --> 00:41:02,320
The locations you only need to pass in name and then it'll create that.

265
00:41:02,320 --> 00:41:07,320
So the locations is actually a bit more efficient.

266
00:41:07,320 --> 00:41:13,320
But the strains, for example.

267
00:41:13,320 --> 00:41:21,320
At the moment. This object is essentially what you'll want to pass from your user interface.

268
00:41:21,320 --> 00:41:31,320
So in your user interface, you essentially need some way for the user to enter the name.

269
00:41:31,320 --> 00:41:37,320
The testing status. H.C. level. C.B.D. level.

270
00:41:37,320 --> 00:41:43,320
Indicator percentage and sativa percentage.

271
00:41:43,320 --> 00:41:46,320
There.

272
00:41:46,320 --> 00:41:56,320
The models actually have.

273
00:41:56,320 --> 00:42:03,320
A from method. So.

274
00:42:03,320 --> 00:42:09,320
So for any model, so you could, for example, you could essentially do strain.

275
00:42:09,320 --> 00:42:13,320
Great from Jason.

276
00:42:13,320 --> 00:42:17,320
And you'd pass it the client.

277
00:42:17,320 --> 00:42:20,320
Your license number.

278
00:42:20,320 --> 00:42:22,320
And the object.

279
00:42:22,320 --> 00:42:26,320
And then this object can be in.

280
00:42:26,320 --> 00:42:30,320
Camel case or snake case.

281
00:42:30,320 --> 00:42:36,320
But it's still once again, you're still needing to pass it the object.

282
00:42:36,320 --> 00:42:37,320
So.

283
00:42:37,320 --> 00:42:42,320
To answer your question, Charles.

284
00:42:42,320 --> 00:42:48,320
You still need to get this object from somewhere.

285
00:42:48,320 --> 00:42:52,320
So.

286
00:42:52,320 --> 00:43:06,320
From my experience, just if you could boil the user interface down to just passing Jason objects, then it makes it quite pluggable because now.

287
00:43:06,320 --> 00:43:15,320
Anyone can make any sort of user interface, right? You could use like, you know, you could use any JavaScript framework you wanted.

288
00:43:15,320 --> 00:43:19,320
You could use.

289
00:43:19,320 --> 00:43:23,320
Yeah, exactly. So you could use letter.

290
00:43:23,320 --> 00:43:28,320
You know, so there's some other alternatives, but essentially any JavaScript framework.

291
00:43:28,320 --> 00:43:36,320
And you'll just basically have a form where you just have the user enter their name.

292
00:43:36,320 --> 00:43:41,320
And then these other fields.

293
00:43:41,320 --> 00:43:54,320
And then you would just pass your Jason object and then here in your code and your Python code, you basically just get your strain and then you can just.

294
00:43:54,320 --> 00:43:59,320
Pass that strain to your client.

295
00:43:59,320 --> 00:44:01,320
So.

296
00:44:01,320 --> 00:44:10,320
Why even do this? Well, it's basically just so you, you know, you don't have to.

297
00:44:10,320 --> 00:44:24,320
Wait a second. Let me just navigate.

298
00:44:24,320 --> 00:44:33,320
It's basically just it just supposed to be just a nice shortcut around having to do this every time.

299
00:44:33,320 --> 00:44:37,320
So basically.

300
00:44:37,320 --> 00:44:43,320
The benefits to the client are it creates a session.

301
00:44:43,320 --> 00:44:47,320
So that way you've got essentially this open.

302
00:44:47,320 --> 00:44:53,320
You've got like a connection established between your.

303
00:44:53,320 --> 00:44:59,320
Piece of software and metric that way your requests.

304
00:44:59,320 --> 00:45:04,320
You know, can go as fast as possible and then.

305
00:45:04,320 --> 00:45:11,320
And then it essentially just wraps, you know, a bunch of helper functions around the request.

306
00:45:11,320 --> 00:45:20,320
Because basically you're not doing anything fancy here. You just you're making just a record. So this is.

307
00:45:20,320 --> 00:45:40,320
It's a big complex, but it's basically you're basically just making a request to that.

308
00:45:40,320 --> 00:45:51,320
Let's open it over.

309
00:45:51,320 --> 00:46:20,320
Okay, the client. Okay, so the requests. Okay, so this request is not really that much different than essentially just saying import requests.

310
00:46:20,320 --> 00:46:30,320
Essentially you're just essentially you're just doing import requests.

311
00:46:30,320 --> 00:46:34,320
You know, requests. Get.

312
00:46:34,320 --> 00:46:43,320
You know, your URL.

313
00:46:43,320 --> 00:46:51,320
Basically, just doing that, and then you may pass it some parameters.

314
00:46:51,320 --> 00:46:56,320
So your parameters.

315
00:46:56,320 --> 00:46:59,320
That's where you actually.

316
00:46:59,320 --> 00:47:05,320
You actually can pass filters, and so that would.

317
00:47:05,320 --> 00:47:14,320
And.

318
00:47:14,320 --> 00:47:34,320
So, like, for example, a metric one, I think they've got like something like last modified.

319
00:47:34,320 --> 00:47:51,320
And you want to double check this.

320
00:47:51,320 --> 00:47:56,320
Anyways.

321
00:47:56,320 --> 00:48:04,320
So, I'm going to put some filters in my test case.

322
00:48:04,320 --> 00:48:12,320
You may try some of these filters.

323
00:48:12,320 --> 00:48:38,320
So, you're basically you're doing that, and then you're saying.

324
00:48:38,320 --> 00:48:49,320
So this is essentially would be like the simplest way to make a request to the metric API, you would.

325
00:48:49,320 --> 00:49:02,320
You know, you would basically get your endpoint.

326
00:49:02,320 --> 00:49:10,320
If you were just like trying to get started right out of the box.

327
00:49:10,320 --> 00:49:15,320
That's essentially you wouldn't even you wouldn't you wouldn't even pass it parameters.

328
00:49:15,320 --> 00:49:31,320
So.

329
00:49:31,320 --> 00:49:47,320
That would.

330
00:49:47,320 --> 00:49:52,320
Yes, and so.

331
00:49:52,320 --> 00:50:02,320
That would essentially give you your response. So this would basically be.

332
00:50:02,320 --> 00:50:06,320
You know, your simplest request to the metric API.

333
00:50:06,320 --> 00:50:13,320
And then, you know, your data would be, you know, your response.

334
00:50:13,320 --> 00:50:28,320
And then, you know, with this, you know, with with like, you know, less than 10 lines of code. I mean, honestly, about five lines of code, really.

335
00:50:28,320 --> 00:50:36,320
Yeah, so with like five lines of code, you know, you can make a request to the to the metric API, and there's nothing wrong with that.

336
00:50:36,320 --> 00:50:43,320
That's an awesome way to go when you're getting started.

337
00:50:43,320 --> 00:50:52,320
When you're, you know, you're you're actually, you know, setting up your user interface, you know, you're going to maybe.

338
00:50:52,320 --> 00:50:58,320
Find you're going to be repeating some things. So, for example, some try and accepts.

339
00:50:58,320 --> 00:51:14,320
So here, you're this is sort of restarting the session if the session ends and then down here, we're checking the response because.

340
00:51:14,320 --> 00:51:23,320
When you get in next week, I want to get the green light that I can actually show you the test environment in.

341
00:51:23,320 --> 00:51:30,320
In action, so I actually want to check with metric if I'm it can actually demo the test environment or not.

342
00:51:30,320 --> 00:51:35,320
I'm not certain I can. That's why I'm just showing you the code for today.

343
00:51:35,320 --> 00:51:43,320
But next week, I would love to show you this in, you know, in action, so to speak.

344
00:51:43,320 --> 00:51:46,320
But I just want to get the green light first.

345
00:51:46,320 --> 00:51:55,320
That I may not so, but long story short is real quick, like, you know, you can just get the response.

346
00:51:55,320 --> 00:52:06,320
You know, you can check its status code.

347
00:52:06,320 --> 00:52:17,320
So that'll raise an error if if you didn't have a 200 status code, but essentially.

348
00:52:17,320 --> 00:52:20,320
Essentially.

349
00:52:20,320 --> 00:52:32,320
This is sort of just how the client grew, you know, basically, you know, you start with just this simple import requests, but I just sort of knew that this was just going to be, you know.

350
00:52:32,320 --> 00:52:44,320
You know, just a simple wrapper around an API. So you're just going to need some some basic things like, you know, a session just to be efficient and, you know, some try accepts.

351
00:52:44,320 --> 00:52:50,320
And so that way you just have this built in exception class.

352
00:52:50,320 --> 00:53:03,320
And this is it's just basically a cop copied this from G spread, but it's basically just a fancy way to get the error message.

353
00:53:03,320 --> 00:53:06,320
So.

354
00:53:06,320 --> 00:53:22,320
So basically when metric errors out, you know, they may give you different types of error responses. And so this is just a fancy way to handle all the cases that way you can get a nice error message printed out.

355
00:53:22,320 --> 00:53:27,320
No matter what the error is.

356
00:53:27,320 --> 00:53:36,320
And then just and then it just, you know, returns models. So that way.

357
00:53:36,320 --> 00:53:56,320
The models are an attempt to be sort of self sufficient. They're not entirely, but you can think of the models as souped up dictionaries. So they're basically just dictionaries that can also, they also have a couple built in methods.

358
00:53:56,320 --> 00:54:04,320
So they basically can self update and self delete.

359
00:54:04,320 --> 00:54:18,320
So that's that that's the main thing. And just from a programming point of view, it can be useful because so here, this is still a long answer to your question, Charles.

360
00:54:18,320 --> 00:54:28,320
But basically in your user interface, you've got your form, where you create the strain.

361
00:54:28,320 --> 00:54:36,320
This is done on your back end, where you basically just get the strains ID and save it in your database.

362
00:54:36,320 --> 00:54:55,320
And then on the user interface, if say you then, you know, change one of the form fields. Well, then you just, you know, just run this function on your back end and it just updates the strain.

363
00:54:55,320 --> 00:55:11,320
And then if they like click the delete button, you know, you could say they called the delete button. You could just do, oh, you know, we want to do.

364
00:55:11,320 --> 00:55:17,320
You know, you can just you can basically just self delete these things.

365
00:55:17,320 --> 00:55:28,320
So the long story short is primarily meant to be used, you know, programmatically.

366
00:55:28,320 --> 00:55:41,320
So whether you plug, you know, plug this package up to your user interface or you just want to, you know, just write some scripts and you just you just need a helper.

367
00:55:41,320 --> 00:56:00,320
I've just found this. I just found creating abstractions quite useful because so let's let's take it all back to the ground floor here here in the end.

368
00:56:00,320 --> 00:56:13,320
Because what are we trying to do? We're trying to make your workflow as simple as possible. So we just looked under the hood.

369
00:56:13,320 --> 00:56:26,320
And so don't, you know, don't be scared or anything. Just look at G spread. I pretty much just copied the work there and

370
00:56:26,320 --> 00:56:34,320
it ended up being a little complex. But, you know, as far as rappers go, it's it's not the most complex rapper in the world.

371
00:56:34,320 --> 00:56:39,320
I try to keep it as simple as possible. And then.

372
00:56:39,320 --> 00:56:48,320
I've just found that creating rappers helps in your workflow because then you can just you can abstract away all that complexity.

373
00:56:48,320 --> 00:57:01,320
So this is actually a programming philosophy that I've learned recently is you want to you want to program to an interface.

374
00:57:01,320 --> 00:57:15,320
So this is an interface, the metric. So we're basically all we're doing is saying, get plans, you know, manicure, harvest, move.

375
00:57:15,320 --> 00:57:24,320
You know. Get packages, get receipts. So there's no logic here. That's our interface.

376
00:57:24,320 --> 00:57:37,320
And then. This is what's called our implementation. And so this is where we've actually implemented our request to the metric API.

377
00:57:37,320 --> 00:57:52,320
And so our interface doesn't care about our implementation. So it's brilliant because you can set up your, you know, your user interface facing code.

378
00:57:52,320 --> 00:58:01,320
So that way, you know, you just call it get packages. But let's say, you know, get packages needs to be improved or refactored.

379
00:58:01,320 --> 00:58:12,320
Well, you just go and. You can, you know, you can.

380
00:58:12,320 --> 00:58:19,320
Fix or improve the implementation of get packages.

381
00:58:19,320 --> 00:58:31,320
But then everybody who's using get packages to say you're using the CanLytics module, then you don't have to change anything. You're good to go.

382
00:58:31,320 --> 00:58:38,320
You can update to the next version and everything just just still keeps working.

383
00:58:38,320 --> 00:58:52,320
You know, so we can, you know, if we need to fix an error, you see here we're doing some error catching, you know, in case, you know, some basically this catches it in case.

384
00:58:52,320 --> 00:59:02,320
Oh, does it return a list? Does it return an object or does it return nothing at all?

385
00:59:02,320 --> 00:59:16,320
And so, you know, say we need to add error catching to create packages later. Well, we can change the implementation and then the interface stays the same.

386
00:59:16,320 --> 00:59:25,320
So that's sort of a design pattern that I've learned recently, and it's been incredibly effective.

387
00:59:25,320 --> 00:59:42,320
It just makes writing software a dream. And next week I'll show you the the CanLytics console, which is actually trying to do what I just described, which is take

388
00:59:42,320 --> 01:00:00,320
this engine, which is an interface, a programming interface to the metric API, so an API to an API. But next week I'm going to try to show you how I've plugged it up to the CanLytics console.

389
01:00:00,320 --> 01:00:25,320
So that way users actually can enter in their strains and everything. So next week, perhaps, I can show you how instead of doing strain creation and management or package creation and management

390
01:00:25,320 --> 01:00:35,320
programmatically, we can basically wire this up to a user interface.

391
01:00:35,320 --> 01:00:46,320
Any user interface, so you can make any HTML, JavaScript, Flutter, Dart, Android,

392
01:00:46,320 --> 01:00:50,320
iOS, anything, right?

393
01:00:50,320 --> 01:01:01,320
Well, those may not be running Python, but you can plug this up to any user interface.

394
01:01:01,320 --> 01:01:12,320
Well, in those cases, you would just be making requests, but plug it up to any user interface and you're off to the races.

395
01:01:12,320 --> 01:01:28,320
So I just thought I would share that with you today. I know we were going to talk about statistics, so we may have to get back to that next week, because they also have some really cool data that Charles has put together on

396
01:01:28,320 --> 01:01:31,320
Waste Analytics.

397
01:01:31,320 --> 01:01:41,320
But I think we're out of time for today, so I can show you more of Charles' awesome work next week.

398
01:01:41,320 --> 01:01:53,320
So just to wrap up today, are there any final questions, comments, thoughts, or anything?

399
01:01:53,320 --> 01:02:06,320
Now, I do need to go back and fix a couple of the, I think a couple of things in that import code, but at least make it more efficient.

400
01:02:06,320 --> 01:02:15,320
Yes, and that's where you've hit on the important principle of refactoring, where, I mean, that's the way software goes.

401
01:02:15,320 --> 01:02:22,320
I mean, let's think about how far computers have gone. You basically just need to start somewhere.

402
01:02:22,320 --> 01:02:33,320
So you just need to start with some code that works, right? So you wrote some code that imports that laboratory data.

403
01:02:33,320 --> 01:02:47,320
So awesome work, Charles. And so, as you noted, it can get hightied up, it can become more efficient, and it can gradually be improved.

404
01:02:47,320 --> 01:02:58,320
And that's what you always have to be doing to your code is just keep improving it, keep refactoring it.

405
01:02:58,320 --> 01:03:07,320
And that's how your code evolves over time. And so, but you have to start somewhere.

406
01:03:07,320 --> 01:03:17,320
And so thank you for making those contributions. And so, Analytics now has its first contributor.

407
01:03:17,320 --> 01:03:29,320
So thank you, Charles. And we're excited to really, you know, really, you know, this is really speeding up. So it's really exciting.

408
01:03:29,320 --> 01:03:45,320
And, yeah, it's exciting to put these tools out to try to be helpful.

409
01:03:45,320 --> 01:04:02,320
And, you know, we can all tinker on it. So if you're open to it, I may actually maybe adjust some of your comments and things just to sort of fit sort of the style of the project.

410
01:04:02,320 --> 01:04:20,320
But I think the code looks good and functions good. So if you want to tinker on it any more than by all means, but I just want to really thank you, Charles, because that is a huge contribution.

411
01:04:20,320 --> 01:04:34,320
Like I said, that's one of the most valuable things for laboratories is to be able to get that data and import it into your into the laboratories database with the click of a button.

412
01:04:34,320 --> 01:04:37,320
No data entry.

413
01:04:37,320 --> 01:04:50,320
Click a button. All that data is in your system, ready to go. You know, let your analysts spend time doing science, not doing data entry.

414
01:04:50,320 --> 01:04:58,320
Yeah. Thank you for contributing to Cantilex, Charles.

415
01:04:58,320 --> 01:05:08,320
Great. Well, we've run a little long. So I'm going to go ahead and wrap it up here just

416
01:05:08,320 --> 01:05:22,320
just so I don't keep you all day. But thank you for coming and checking out the latest the latest developments on cannabis data science.

417
01:05:22,320 --> 01:05:28,320
Are you crew. Thank you.

418
01:05:28,320 --> 01:05:55,320
Feel free to reach out through email or or messages if you know you have any thoughts or or Nick if you start to use it and you run into any questions or anything just reach out because I want to get this this tool in your hands so you can find find some use for it.

419
01:05:55,320 --> 01:05:57,320
Yeah, we'll do.

420
01:05:57,320 --> 01:05:59,320
Great. Awesome.

421
01:05:59,320 --> 01:06:02,320
Alright, everyone, until next week.

422
01:06:02,320 --> 01:06:04,320
Okay.

423
01:06:04,320 --> 01:06:31,320
Last one.

