Skip to content

Commit d437f87

Browse files
add digikey api stock info provider
1 parent 9163137 commit d437f87

23 files changed

+2159
-275
lines changed

Diff for: Makefile

+8-1
Original file line numberDiff line numberDiff line change
@@ -483,7 +483,9 @@ SRC_IMP = \
483483
src/util/kicad_package_parser.cpp\
484484
src/widgets/pool_browser_button.cpp\
485485
src/widgets/pool_browser_parametric.cpp\
486+
src/util/stock_info_provider.cpp\
486487
src/util/stock_info_provider_partinfo.cpp\
488+
src/util/stock_info_provider_digikey.cpp\
487489
src/util/http_client.cpp\
488490
src/widgets/column_chooser.cpp\
489491
src/util/csv_util.cpp\
@@ -662,7 +664,10 @@ SRC_POOL_PRJ_MGR = \
662664
src/pool-prj-mgr/preferences/preferences_window_keys.cpp\
663665
src/pool-prj-mgr/preferences/preferences_window_in_tool_keys.cpp\
664666
src/pool-prj-mgr/preferences/preferences_window_canvas.cpp\
665-
src/pool-prj-mgr/preferences/preferences_window_partinfo.cpp\
667+
src/pool-prj-mgr/preferences/preferences_window_stock_info.cpp\
668+
src/pool-prj-mgr/preferences/preferences_window_stock_info_partinfo.cpp\
669+
src/pool-prj-mgr/preferences/preferences_window_stock_info_digikey.cpp\
670+
src/pool-prj-mgr/preferences/digikey_auth_window.cpp\
666671
src/pool-prj-mgr/preferences/preferences_window_misc.cpp\
667672
src/pool-prj-mgr/preferences/action_editor.cpp\
668673
src/imp/action.cpp\
@@ -680,7 +685,9 @@ SRC_POOL_PRJ_MGR = \
680685
src/pool-prj-mgr/welcome_window.cpp\
681686
src/pool-prj-mgr/output_window.cpp\
682687
src/pool-prj-mgr/autosave_recovery_dialog.cpp\
688+
src/util/stock_info_provider.cpp\
683689
src/util/stock_info_provider_partinfo.cpp\
690+
src/util/stock_info_provider_digikey.cpp\
684691
src/widgets/help_button.cpp\
685692
src/pool-prj-mgr/pool-mgr/kicad_symbol_import_wizard/kicad_symbol_import_wizard.cpp\
686693
src/pool-prj-mgr/pool-mgr/kicad_symbol_import_wizard/gate_editor.cpp\

Diff for: imp.gresource.xml

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
<file>pool-prj-mgr/pools_window/pool_merge.ui</file>
3131
<file>pool-prj-mgr/pools_window/pool_download_window.ui</file>
3232
<file>util/stock_info_provider_partinfo_schema.sql</file>
33+
<file>util/stock_info_provider_digikey_schema.sql</file>
34+
<file>pool-prj-mgr/preferences/digikey_auth_window.ui</file>
3335

3436
<file preprocess="xml-stripblanks">icons/scalable/actions/edit-schematic-symbolic.svg</file>
3537
<file preprocess="xml-stripblanks">icons/scalable/actions/edit-board-symbolic.svg</file>

Diff for: src/imp/bom_export_window.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include "widgets/pool_browser_parametric.hpp"
1010
#include "preferences/preferences_provider.hpp"
1111
#include "preferences/preferences.hpp"
12-
#include "util/stock_info_provider_partinfo.hpp"
12+
#include "util/stock_info_provider.hpp"
1313

1414
namespace horizon {
1515

@@ -392,8 +392,7 @@ void BOMExportWindow::update_concrete_parts()
392392
browser_param->search();
393393
float tol = rb_tol_10->get_active() ? .1 : .01;
394394
browser_param->filter_similar(part->uuid, tol);
395-
if (PreferencesProvider::get_prefs().partinfo.is_enabled()) {
396-
auto prv = std::make_unique<StockInfoProviderPartinfo>(pool.get_base_path());
395+
if (auto prv = StockInfoProvider::create(pool.get_base_path())) {
397396
browser_param->add_stock_info_provider(std::move(prv));
398397
}
399398
browser_param->search();

Diff for: src/pool-prj-mgr/pool-prj-mgr-main.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <windows.h>
77
#endif
88
#include "util/automatic_prefs.hpp"
9+
#include "util/stock_info_provider.hpp"
910

1011
int main(int argc, char *argv[])
1112
{
@@ -19,6 +20,7 @@ int main(int argc, char *argv[])
1920
horizon::create_config_dir();
2021
horizon::PoolManager::init();
2122
horizon::AutomaticPreferences::get();
23+
horizon::StockInfoProvider::init_db();
2224
horizon::install_signal_exception_handler();
2325

2426
// Start the application, showing the initial window,

Diff for: src/pool-prj-mgr/preferences/digikey_auth_window.cpp

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#include "digikey_auth_window.hpp"
2+
#include "util/gtk_util.hpp"
3+
#include "util/util.hpp"
4+
#include "util/sqlite.hpp"
5+
#include "util/uuid.hpp"
6+
#include "util/http_client.hpp"
7+
#include "util/win32_undef.hpp"
8+
#include <iostream>
9+
#include "preferences/preferences.hpp"
10+
#include "preferences/preferences_provider.hpp"
11+
#include "util/stock_info_provider_digikey.hpp"
12+
#include "util/str_util.hpp"
13+
14+
namespace horizon {
15+
16+
DigiKeyAuthWindow *DigiKeyAuthWindow::create()
17+
{
18+
DigiKeyAuthWindow *w;
19+
Glib::RefPtr<Gtk::Builder> x = Gtk::Builder::create();
20+
x->add_from_resource("/org/horizon-eda/horizon/pool-prj-mgr/preferences/digikey_auth_window.ui");
21+
x->get_widget_derived("window", w);
22+
return w;
23+
}
24+
25+
static const std::string redirect_uri = "https://horizon-eda.org/oauth.html";
26+
27+
DigiKeyAuthWindow::DigiKeyAuthWindow(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &x)
28+
: Gtk::Window(cobject), preferences(PreferencesProvider::get_prefs().digikey_api)
29+
{
30+
GET_WIDGET(code_entry);
31+
code_entry->signal_changed().connect(sigc::mem_fun(*this, &DigiKeyAuthWindow::update_buttons));
32+
{
33+
Gtk::Button *open_browser_button;
34+
GET_WIDGET(open_browser_button);
35+
open_browser_button->signal_clicked().connect([this] {
36+
const std::string uri = "https://api.digikey.com/v1/oauth2/authorize?response_type=code&redirect_uri="
37+
+ Glib::uri_escape_string(redirect_uri) + "&client_id=" + preferences.client_id;
38+
gtk_show_uri_on_window(gobj(), uri.c_str(), GDK_CURRENT_TIME, nullptr);
39+
});
40+
}
41+
42+
GET_WIDGET(cancel_button);
43+
cancel_button->signal_clicked().connect([this] { close(); });
44+
45+
GET_WIDGET(login_button);
46+
login_button->signal_clicked().connect(sigc::mem_fun(*this, &DigiKeyAuthWindow::handle_login));
47+
48+
49+
{
50+
Gtk::Label *status_label = nullptr;
51+
GET_WIDGET(status_label);
52+
Gtk::Spinner *status_spinner = nullptr;
53+
GET_WIDGET(status_spinner);
54+
Gtk::Revealer *status_revealer = nullptr;
55+
GET_WIDGET(status_revealer);
56+
status_dispatcher.attach(status_label);
57+
status_dispatcher.attach(status_spinner);
58+
status_dispatcher.attach(status_revealer);
59+
status_dispatcher.attach(this);
60+
status_dispatcher.signal_notified().connect([this](const StatusDispatcher::Notification &n) {
61+
is_busy = n.status == StatusDispatcher::Status::BUSY;
62+
update_buttons();
63+
64+
if (n.status == StatusDispatcher::Status::DONE) {
65+
Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &DigiKeyAuthWindow::close), 1000);
66+
}
67+
});
68+
}
69+
signal_delete_event().connect([this](GdkEventAny *ev) { return is_busy; });
70+
71+
update_buttons();
72+
signal_hide().connect([this] { delete this; });
73+
}
74+
75+
void DigiKeyAuthWindow::update_buttons()
76+
{
77+
cancel_button->set_sensitive(!is_busy);
78+
login_button->set_sensitive(!is_busy && code_entry->get_text().size());
79+
}
80+
81+
void DigiKeyAuthWindow::handle_login()
82+
{
83+
client_id = preferences.client_id;
84+
client_secret = preferences.client_secret;
85+
code = code_entry->get_text();
86+
trim(code);
87+
status_dispatcher.reset("Starting…");
88+
89+
auto thr = std::thread(&DigiKeyAuthWindow::worker, this);
90+
thr.detach();
91+
}
92+
93+
void DigiKeyAuthWindow::worker()
94+
{
95+
status_dispatcher.set_status(StatusDispatcher::Status::BUSY, "Getting token");
96+
try {
97+
HTTP::Client client;
98+
SQLite::Database db(StockInfoProviderDigiKey::get_db_filename(), SQLITE_OPEN_READWRITE);
99+
const std::string postdata = "client_id=" + client_id + "&client_secret=" + client_secret
100+
+ "&grant_type=authorization_code&code=" + code
101+
+ "&redirect_uri=" + Glib::uri_escape_string(redirect_uri);
102+
const auto resp = json::parse(client.post("https://api.digikey.com/v1/oauth2/token", postdata));
103+
db.execute("BEGIN IMMEDIATE");
104+
StockInfoProviderDigiKey::update_tokens_from_response(db, resp);
105+
db.execute("COMMIT");
106+
status_dispatcher.set_status(StatusDispatcher::Status::DONE, "Done");
107+
}
108+
catch (const std::exception &e) {
109+
status_dispatcher.set_status(StatusDispatcher::Status::ERROR, e.what());
110+
}
111+
catch (...) {
112+
status_dispatcher.set_status(StatusDispatcher::Status::ERROR, "Unknown error");
113+
}
114+
}
115+
116+
} // namespace horizon

Diff for: src/pool-prj-mgr/preferences/digikey_auth_window.hpp

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#pragma once
2+
#include <gtkmm.h>
3+
#include "util/status_dispatcher.hpp"
4+
5+
namespace horizon {
6+
class DigiKeyAuthWindow : public Gtk::Window {
7+
public:
8+
static DigiKeyAuthWindow *create();
9+
DigiKeyAuthWindow(BaseObjectType *cobject, const Glib::RefPtr<Gtk::Builder> &x);
10+
11+
private:
12+
const class DigiKeyApiPreferences &preferences;
13+
Gtk::Entry *code_entry = nullptr;
14+
Gtk::Button *login_button = nullptr;
15+
Gtk::Button *cancel_button = nullptr;
16+
17+
std::string client_id;
18+
std::string client_secret;
19+
std::string code;
20+
void worker();
21+
void handle_login();
22+
bool is_busy = false;
23+
void update_buttons();
24+
25+
StatusDispatcher status_dispatcher;
26+
};
27+
} // namespace horizon

Diff for: src/pool-prj-mgr/preferences/digikey_auth_window.ui

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- Generated with glade 3.38.2 -->
3+
<interface>
4+
<requires lib="gtk+" version="3.24"/>
5+
<object class="GtkWindow" id="window">
6+
<property name="can-focus">False</property>
7+
<property name="modal">True</property>
8+
<property name="type-hint">dialog</property>
9+
<child>
10+
<object class="GtkBox">
11+
<property name="visible">True</property>
12+
<property name="can-focus">False</property>
13+
<property name="halign">center</property>
14+
<property name="valign">center</property>
15+
<property name="margin-start">20</property>
16+
<property name="margin-end">20</property>
17+
<property name="margin-top">20</property>
18+
<property name="margin-bottom">20</property>
19+
<property name="orientation">vertical</property>
20+
<property name="spacing">10</property>
21+
<child>
22+
<object class="GtkBox">
23+
<property name="visible">True</property>
24+
<property name="can-focus">False</property>
25+
<property name="spacing">10</property>
26+
<child>
27+
<object class="GtkLabel">
28+
<property name="visible">True</property>
29+
<property name="can-focus">False</property>
30+
<property name="label" translatable="yes">&lt;b&gt;Step 1:&lt;/b&gt; Log in and copy code</property>
31+
<property name="use-markup">True</property>
32+
<property name="xalign">0</property>
33+
</object>
34+
<packing>
35+
<property name="expand">True</property>
36+
<property name="fill">True</property>
37+
<property name="position">0</property>
38+
</packing>
39+
</child>
40+
<child>
41+
<object class="GtkButton" id="open_browser_button">
42+
<property name="label" translatable="yes">Open Browser</property>
43+
<property name="visible">True</property>
44+
<property name="can-focus">True</property>
45+
<property name="receives-default">True</property>
46+
<property name="halign">end</property>
47+
</object>
48+
<packing>
49+
<property name="expand">False</property>
50+
<property name="fill">True</property>
51+
<property name="position">2</property>
52+
</packing>
53+
</child>
54+
</object>
55+
<packing>
56+
<property name="expand">False</property>
57+
<property name="fill">True</property>
58+
<property name="position">0</property>
59+
</packing>
60+
</child>
61+
<child>
62+
<object class="GtkBox">
63+
<property name="visible">True</property>
64+
<property name="can-focus">False</property>
65+
<property name="spacing">10</property>
66+
<child>
67+
<object class="GtkLabel">
68+
<property name="visible">True</property>
69+
<property name="can-focus">False</property>
70+
<property name="label" translatable="yes">&lt;b&gt;Step 2:&lt;/b&gt; Paste code</property>
71+
<property name="use-markup">True</property>
72+
<property name="xalign">0</property>
73+
</object>
74+
<packing>
75+
<property name="expand">True</property>
76+
<property name="fill">True</property>
77+
<property name="position">0</property>
78+
</packing>
79+
</child>
80+
<child>
81+
<object class="GtkEntry" id="code_entry">
82+
<property name="visible">True</property>
83+
<property name="can-focus">True</property>
84+
</object>
85+
<packing>
86+
<property name="expand">False</property>
87+
<property name="fill">True</property>
88+
<property name="position">2</property>
89+
</packing>
90+
</child>
91+
</object>
92+
<packing>
93+
<property name="expand">False</property>
94+
<property name="fill">True</property>
95+
<property name="position">1</property>
96+
</packing>
97+
</child>
98+
<child>
99+
<object class="GtkRevealer" id="status_revealer">
100+
<property name="visible">True</property>
101+
<property name="can-focus">False</property>
102+
<property name="transition-type">crossfade</property>
103+
<child>
104+
<object class="GtkBox">
105+
<property name="visible">True</property>
106+
<property name="can-focus">False</property>
107+
<property name="spacing">10</property>
108+
<child>
109+
<object class="GtkSpinner" id="status_spinner">
110+
<property name="visible">True</property>
111+
<property name="can-focus">False</property>
112+
</object>
113+
<packing>
114+
<property name="expand">False</property>
115+
<property name="fill">True</property>
116+
<property name="position">0</property>
117+
</packing>
118+
</child>
119+
<child>
120+
<object class="GtkLabel" id="status_label">
121+
<property name="visible">True</property>
122+
<property name="can-focus">False</property>
123+
<property name="label" translatable="yes">label</property>
124+
<property name="wrap">True</property>
125+
<property name="max-width-chars">0</property>
126+
<property name="xalign">0</property>
127+
</object>
128+
<packing>
129+
<property name="expand">True</property>
130+
<property name="fill">True</property>
131+
<property name="position">1</property>
132+
</packing>
133+
</child>
134+
</object>
135+
</child>
136+
</object>
137+
<packing>
138+
<property name="expand">False</property>
139+
<property name="fill">True</property>
140+
<property name="position">2</property>
141+
</packing>
142+
</child>
143+
</object>
144+
</child>
145+
<child type="titlebar">
146+
<object class="GtkHeaderBar">
147+
<property name="visible">True</property>
148+
<property name="can-focus">False</property>
149+
<property name="title" translatable="yes">Log in to Digi-Key</property>
150+
<property name="has-subtitle">False</property>
151+
<child>
152+
<object class="GtkButton" id="cancel_button">
153+
<property name="label" translatable="yes">Cancel</property>
154+
<property name="visible">True</property>
155+
<property name="can-focus">True</property>
156+
<property name="receives-default">True</property>
157+
</object>
158+
</child>
159+
<child>
160+
<object class="GtkButton" id="login_button">
161+
<property name="label" translatable="yes">Log In</property>
162+
<property name="visible">True</property>
163+
<property name="can-focus">True</property>
164+
<property name="receives-default">True</property>
165+
<style>
166+
<class name="suggested-action"/>
167+
</style>
168+
</object>
169+
<packing>
170+
<property name="pack-type">end</property>
171+
<property name="position">1</property>
172+
</packing>
173+
</child>
174+
</object>
175+
</child>
176+
</object>
177+
</interface>

0 commit comments

Comments
 (0)